Post List API
API endpoint that enables a list of posts to be browsed including related models data and auxhiliary properties.
The configured endpoint purposefully does not permit a POST method as I do not want others creating new posts in the database.
GET /api/blog/posts/
https://waynelambert.dev/api/blog/posts/?limit=3&offset=3", "previous": null, "results": [ { "id": 22, "title": "How to Implement List View Pagination with Django", "slug": "how-to-implement-list-view-pagination-with-django", "content": "<p>Pagination is an important method to restrict the number of database objects in any given view. Pagination implemented well enables users to navigate your content as they need it in addition to keeping your site efficient by only rendering an object's assets required for a given page.</p>\r\n<p>For my use case and the example documented here, I would like to present 6 blog posts at a time unless there are 3 or less remaining posts. In that case, I will present between 7 and 9 posts on the final paginated page.</p>\r\n<p>Pagination will often want to be re-used for the different views that you will present to your visitors. In the case of blog posts, you may wish to present:</p>\r\n<ul>\r\n<li>Posts - by author</li>\r\n<li>Posts - by category</li>\r\n<li>Posts - by date period (perhaps by month or year)</li>\r\n</ul>\r\n<p>The approach and technologies used are Django's class-based views (CBVs), Django's ORM queries and Bootstrap styling to make it a fancy paginator. We'll also use DataGrip to inspect the database schema to give a clear understanding of how the main tables for the project are connected.</p>\r\n<p>As the number of posts grows, it will be increasingly important to reduce the number of queries made to the database, especially when your database models have relationships with other models.</p>\r\n<p>We will need to use Django's <span style=\"font-family: 'Ubuntu Mono', monospace;\">prefetch_related</span> and <span style=\"font-family: 'Ubuntu Mono', monospace;\">select_related</span> methods to reduce the number of queries made to the database.</p>\r\n<p>Let's start by taking a look at the important parts of the models. There are 4 important models within the database schema to consider.</p>\r\n<table style=\"border-collapse: collapse; width: 100%; height: 88px; border-color: #ffffff; border-style: none;\" border=\"1\" cellpadding=\"10\">\r\n<tbody>\r\n<tr>\r\n<td style=\"width: 24.0861%;\"><strong>Django app.Model</strong></td>\r\n<td style=\"width: 1.75439%;\"><strong>Postgres Table<br /></strong></td>\r\n<td style=\"width: 77.4035%;\"><strong>Comments</strong></td>\r\n</tr>\r\n<tr style=\"height: 22px;\">\r\n<td style=\"width: 24.0861%; height: 22px;\">auth.User</td>\r\n<td style=\"width: 1.75439%; height: 22px;\">auth_user</td>\r\n<td style=\"width: 77.4035%; height: 22px;\">This is Django's standard user authentication model</td>\r\n</tr>\r\n<tr style=\"height: 22px;\">\r\n<td style=\"width: 24.0861%; height: 22px;\">users.Profile</td>\r\n<td style=\"width: 1.75439%; height: 22px;\">users_profile</td>\r\n<td style=\"width: 77.4035%; height: 22px;\">This has a 1-to-1 relationship with the User model</td>\r\n</tr>\r\n<tr style=\"height: 22px;\">\r\n<td style=\"width: 24.0861%; height: 22px;\">blog.Category</td>\r\n<td style=\"width: 1.75439%; height: 22px;\">blog_category</td>\r\n<td style=\"width: 77.4035%; height: 22px;\">This has a many-to-many relationship with the Post model</td>\r\n</tr>\r\n<tr style=\"height: 22px;\">\r\n<td style=\"width: 24.0861%; height: 22px;\">blog.Post</td>\r\n<td style=\"width: 1.75439%; height: 22px;\">blog_post</td>\r\n<td style=\"width: 77.4035%; height: 22px;\">Similarly, this has a many-to-many relationship with the Category model. It also has a through relationship to the Profile model using the User model as an intermediary.</td>\r\n</tr>\r\n<tr>\r\n<td style=\"width: 24.0861%;\"> </td>\r\n<td style=\"width: 1.75439%;\">blog_post_categories</td>\r\n<td style=\"width: 77.4035%;\">This isn't explicitly declared in the project. It's constructed by Django's migration by declaring a many-to-many relationship. It's a joining table that holds the <span style=\"font-family: 'Ubuntu Mono', monospace;\">category_id</span> and the <span style=\"font-family: 'Ubuntu Mono', monospace;\">post_id</span> for any instance of a many-to-many relationship.</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p> </p>\r\n<p>Some additional design considerations are:</p>\r\n<ul>\r\n<li>Every time a user is created, a corresponding profile is set up using Django's signals.</li>\r\n<li>It's not possible for a user to delete their profile. This is only possible from the admin back-end with superuser permissions.</li>\r\n<li>A post can only have one author (i.e. user/profile).</li>\r\n<li>A post can belong to many categories and a category can contain many posts, therefore their relationship is a many-to-many one (i.e. uses a ManyToManyField).</li>\r\n</ul>\r\n<p>To illustrate, this is the relevant part of the schema.</p>\r\n<p><img src=\"https://wl-portfolio.s3.eu-west-2.amazonaws.com/post_images/blog-posts-db-schema2.png\" alt=\"Blog Database Schema\" width=\"844\" height=\"718\" /></p>\r\n<p>In terms of the relevant models, they look like this:</p>\r\n<pre class=\"language-python\"><code># ./apps/blog/models.py\r\n\r\n# The fields in this model do not have any attachment to another model\r\nclass Category(models.Model):\r\n name = models.CharField(max_length=16)\r\n slug = models.SlugField(max_length=16, unique=True)\r\n created_date = models.DateTimeField(auto_now_add=True)\r\n\r\n\r\nclass Post(models.Model):\r\n STATUS = (\r\n (0, 'Draft'),\r\n (1, 'Publish')\r\n )\r\n\r\n ... # Other irrelevant fields\r\n\r\n status = models.IntegerField(choices=STATUS, default=0)\r\n author = models.ForeignKey(\r\n get_user_model(), related_name='author', on_delete=models.CASCADE)\r\n categories = models.ManyToManyField(\r\n Category,\r\n related_name='posts',\r\n help_text='Select more than one category by holding down Ctrl or Cmd key'\r\n )\r\n\r\n # Model Managers\r\n objects = models.Manager()\r\n published = PublishedManager()</code></pre>\r\n<pre class=\"language-python\"><code># ./apps/users/models.py\r\n\r\nclass Profile(models.Model):\r\n\r\n class AuthorView(models.IntegerChoices):\r\n USERNAME = 0\r\n FULL_NAME = 1\r\n\r\n user = models.OneToOneField(\r\n get_user_model(), related_name='user', on_delete=models.CASCADE)\r\n ... # Other irrelevant fields/methods</code></pre>\r\n<p>I have also implemented a manager which stores the same queryset which will be used many times throughout the project. Notice that within the <span style=\"font-family: 'Ubuntu Mono', monospace;\">models.py</span> file, the <span style=\"font-family: 'Ubuntu Mono', monospace;\">PublishedManager</span> class was instantiated with the <span style=\"font-family: 'Ubuntu Mono', monospace;\">published</span> model manager variable.</p>\r\n<pre class=\"language-python\"><code># ./apps/blog/managers.py\r\n\r\nfrom django.db import models\r\n\r\n\r\nclass PublishedManager(models.Manager):\r\n def get_queryset(self):\r\n qs = super(PublishedManager, self).get_queryset().filter(status=1)\r\n return qs.prefetch_related('categories').select_related('author__user')</code></pre>\r\n<p>The main <span style=\"font-family: 'Ubuntu Mono', monospace;\">queryset</span> variable is used to drive the behaviour of retrieiving the required posts for any other list views within the project. This retrieves all categories associated with a post (since a post can belong to many categories). It also selects any objects from the <span style=\"font-family: 'Ubuntu Mono', monospace;\">User</span> model associated with the post's <span style=\"font-family: 'Ubuntu Mono', monospace;\">author</span> and the corresponding <span style=\"font-family: 'Ubuntu Mono', monospace;\">related_name</span> reverse relationship defined in the <span style=\"font-family: 'Ubuntu Mono', monospace;\">Profile</span> model. The queryset is then filtered to only include posts with a status of <em>published</em> which is a standard desired view throughout the project since the publication will never want to display draft posts.</p>\r\n<p>The most important part within the <a href=\"https://docs.djangoproject.com/en/3.1/ref/models/querysets/\">QuerySet API Reference page of the Django docs</a> and one to really try to internalise is quoted here:</p>\r\n<blockquote>\r\n<p><span style=\"font-family: 'Ubuntu Mono', monospace;\">select_related</span> works by creating an SQL join and including the fields of the related object in the <span style=\"font-family: 'Ubuntu Mono', monospace;\">SELECT</span> statement. For this reason, select_related gets the related objects in the same database query. However, to avoid the much larger result set that would result from joining across a ‘many’ relationship, <span style=\"font-family: 'Ubuntu Mono', monospace;\">select_related</span> is limited to single-valued relationships - foreign key and one-to-one.<br /><br /><span style=\"font-family: 'Ubuntu Mono', monospace;\">prefetch_related</span>, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related, in addition to the foreign key and one-to-one relationships that are supported by <span style=\"font-family: 'Ubuntu Mono', monospace;\">select_related</span>.</p>\r\n</blockquote>\r\n<p><strong>Implementing the Views</strong></p>\r\n<p>I start with a <span style=\"font-family: 'Ubuntu Mono', monospace;\">PostView</span> which I'm setting up as a parent class so that all of the properties and behaviours can be inherited in select subsequent views since these will be shared for the majority of the other list views within the project.</p>\r\n<pre class=\"language-python\"><code># ./apps/blog/views.py\r\n\r\nclass PostView(ListView):\r\n \"\"\"\r\n Custom view sets default behaviour for all list views to subclass\r\n and inherit for their own implementation\r\n \"\"\"\r\n model = Post\r\n context_object_name = 'posts'\r\n category_list = Category.objects.all().prefetch_related('posts')\r\n extra_context = {'categories_list': category_list}\r\n queryset = Post.published.all()\r\n\r\n def get_context_data(self, **kwargs):\r\n \"\"\" Facilitates pagination and post count summary \"\"\"\r\n context = super(PostView, self).get_context_data(**kwargs)\r\n context['current_page'] = context.pop('page_obj', None)\r\n return context</code></pre>\r\n<p>The <span style=\"font-family: 'Ubuntu Mono', monospace;\">category_list</span> variable included as <span style=\"font-family: 'Ubuntu Mono', monospace;\">extra_context</span> exists so that they can be iterated upon within the site's sidebar. Since the relationship between categories and posts is a many-to-many one, it requires the use of the <span style=\"font-family: 'Ubuntu Mono', monospace;\">prefetch_related</span> method to reduce the number of queries used.</p>\r\n<p>The queryset declaration is simply a reference to the queryset declared within the <span style=\"font-family: 'Ubuntu Mono', monospace;\">published</span> model manager. This means that I do not need to repeat the <span style=\"font-family: 'Ubuntu Mono', monospace;\">prefetch_related</span> and <span style=\"font-family: 'Ubuntu Mono', monospace;\">select_related </span>logic within any of the other views. The manager has also handled the filtering to published posts, therefore this filtering logic does not need to be repeated here.</p>\r\n<p>The <span style=\"font-family: 'Ubuntu Mono', monospace;\">get_context_data</span> method inherits all of the properties and behaviours from Django's generic <span style=\"font-family: 'Ubuntu Mono', monospace;\">ListView</span> and any of its other ancestors. <a href=\"https://ccbv.co.uk/projects/Django/3.0/django.views.generic.list/ListView/\">See Classy Class-Based Views</a> for more detail. The remaining functionality replaces the generic <span style=\"font-family: 'Ubuntu Mono', monospace;\">page_obj</span> context dictionary key with a more user-friendly <span style=\"font-family: 'Ubuntu Mono', monospace;\">current_page</span> one. It can be used for any paginated list view implemented throughout the project. This helps with giving the template logic user-friendly and meaningful names which helps with readability and maintainability of the code in the future.</p>\r\n<hr />\r\n<p>Next up, I define a <span style=\"font-family: 'Ubuntu Mono', monospace;\">HomeView</span> which as the view's name suggests is for the blog's home page. This is how it looks.</p>\r\n<pre class=\"language-python\"><code># ./apps/blogs/views.py\r\n\r\nclass HomeView(PostView):\r\n \"\"\" Drives the list of posts returned on the blog's home page \"\"\"\r\n template_name = 'blog/home.html'\r\n paginate_by = 6\r\n paginate_orphans = 3</code></pre>\r\n<p>Notice how it starts by subclassing from <span style=\"font-family: 'Ubuntu Mono', monospace;\">PostView</span>, therefore inheriting all of the attributes and methods associated with it. This means we will not need to define these again here.</p>\r\n<p>The <span style=\"font-family: 'Ubuntu Mono', monospace;\">paginate_by</span> attribute specifies that there will be 6 objects on any individual paginated page.</p>\r\n<p>The <span style=\"font-family: 'Ubuntu Mono', monospace;\">paginate_orphans</span> attribute specifies that if there are 3 or less remaining objects when the total number of objects is divided by 6, these orphans will also be displayed on the previous page. As an example, if there are 14 posts within the home page's view, there will be 1 page of 6 posts, and 1 page of 8 posts (i.e. 6 ordinarily paginated and 2 orphans). This prevents the 2 isolated posts looking lonely on their own page. 😀</p>\r\n<p>There are other implemented views too which displays the posts by category or author, however they use the same pagination implementation within their respective templates because they also include the below snippet.</p>\r\n<p>The HTML for the pagination looks like:</p>\r\n<pre class=\"language-html\"><code><!-- ./apps/blog/templates/blog/components/pagination_nav.html -->\r\n\r\n<ul class=\"pagination justify-content-center my-5\">\r\n {% if current_page.has_previous %}\r\n <!-- 'First' and 'Previous' Buttons -->\r\n <li class=\"page-item\">\r\n <a class=\"page-link\" href=\"?page=1\">|&lt; First</a>\r\n </li>\r\n <li class=\"page-item\">\r\n <a class=\"page-link\" href=\"?page={{ current_page.previous_page_number }}\">&lt; Previous</a>\r\n </li>\r\n {% endif %}\r\n\r\n <!-- Numbered Buttons -->\r\n {% for page in paginator.page_range %}\r\n {% if current_page.number == page %}\r\n <li class=\"page-item\">\r\n <a class=\"page-link\" href=\"?page={{ page }}\"><strong><u>{{ page }}</u></strong></a>\r\n </li>\r\n {% elif page > current_page.number|add:'-3' and page < current_page.number|add:'3' %}\r\n <li class=\"page-item\">\r\n <a class=\"page-link\" href=\"?page={{ page }}\">{{ page }}</a>\r\n </li>\r\n {% endif %}\r\n {% endfor %}\r\n\r\n <!-- 'Next' and 'Last' Buttons -->\r\n {% if current_page.has_next %}\r\n <li class=\"page-item\">\r\n <a class=\"page-link\" \r\n href=\"?page={{ current_page.next_page_number }}\">Next &gt;</a>\r\n </li>\r\n <li class=\"page-item\">\r\n <a class=\"page-link\" href=\"?page={{ current_page.paginator.num_pages }}\">Last &gt;|</a>\r\n </li>\r\n {% endif %}\r\n</ul></code></pre>\r\n<p>It's included within the relevant template(s) with a simple conditional statement and template inclusion like this:</p>\r\n<pre class=\"language-html\"><code><!-- ./apps/blog/templates/blog/components/pagination_nav.html -->\r\n\r\n{% extends 'base.html' %}\r\n\r\n{% block title %}{{ block.super }} | Blog Home{% endblock title %}\r\n\r\n{% block content %}\r\n\r\n ...\r\n\r\n {% if is_paginated %}{% include 'components/pagination_nav.html' %}{% endif %}\r\n{% endblock content %}</code></pre>\r\n<p>You should now be able to view any list view of objects using pagination. Of course, you can now customise the Sass and HTML for your paginator to your liking.</p>\r\n<p>For reference, this post was written with the GitHub source code referenced at the following commit: <a href=\"https://github.com/WayneLambert/portfolio/tree/6afe13d6b9e43cb84c949943d72eb74450db3656\">6afe13d</a></p>\r\n<p>The post was updated when the project was using Python 3.8.5 and Django 3.1.1. For a complete list of the project's packages, please refer to the <span style=\"font-family: 'Ubuntu Mono', monospace;\">Pipfile</span> at the commit above.</p>", "reference_url": "", "publish_date": "2020-09-07T21:03:41.327842Z", "updated_date": "2020-10-23T13:08:43.747776Z", "image": "https://wl-portfolio.s3.amazonaws.com/post_images/bootstrap-pagination.png", "status": "Publish", "word_count": 1848, "reading_time": 25, "post_absolute_url": "/blog/post/how-to-implement-list-view-pagination-with-django/", "author_username": "wayne-lambert", "author_first_name": "Wayne", "author_last_name": "Lambert", "author_full_name": "Wayne Lambert", "author_initials": "WL", "author_display_name": "Wayne Lambert", "author_join_year": 2019, "author_view": 1, "author_created_date": "2019-07-21T10:02:09.888000Z", "author_updated_date": "2022-12-14T14:44:36.068604Z", "author_absolute_url": "/blog/users/wayne-lambert/profile/", "author_profile_picture": "https://wl-portfolio.s3.amazonaws.com/profile_pics/gravatar-500.jpg", "categories": [ { "id": 11, "name": "Bootstrap", "slug": "bootstrap", "created_date": "2020-09-07T20:58:13.432133Z" }, { "id": 1, "name": "Django", "slug": "django", "created_date": "2019-07-27T21:39:31.016414Z" }, { "id": 3, "name": "PostgreSQL", "slug": "postgresql", "created_date": "2019-07-27T21:39:41.170546Z" } ] }, { "id": 14, "title": "Calculating the Fibonacci Sequence in Python", "slug": "fibonacci-sequence-algorithm-python", "content": "<p>In mathematics, the Fibonacci sequence is built up by summing the previous two numbers in the sequence and appending it to the sequence. The sequence starts with 0 and 1, therefore the first ten numbers of the Fibonacci sequence are: 0, 1, 1, 2, 3, 5, 8, 13, 21, and 34.</p>\r\n<p>There are many methods of implementing this in Python, however I have written three methods of achieving this.</p>\r\n<p><strong><u>Solution 1</u>: Use a for loop to append to a list on the fly.</strong></p>\r\n<div>\r\n<div>\r\n<pre><code class=\"language-python\">def fibonacci_list(previous, current, maximum):\r\n fib_nums = []\r\n for num in range(maximum):\r\n fib_nums.append(previous)\r\n print('Fib', num, '=', previous)\r\n previous, current = current, previous + current\r\n return fib_nums\r\n\r\n# The function can be called as follows:\r\nif __name__ == '__main__':\r\n fibonacci_list(previous=0, current=1, maximum=101)</code></pre>\r\n</div>\r\n</div>\r\n<p><strong><u>Solution 2</u>: Use a while loop to yield each element as it is calculated.</strong></p>\r\n<div>\r\n<div>\r\n<pre><code class=\"language-python\">def fibonacci_generator(previous, current, maximum):\r\n count = 0\r\n while count < maximum:\r\n previous, current = current, previous + current\r\n yield previous\r\n count += 1\r\n print(f'Fib {count} = {previous}')\r\n\r\n# `pass` statement is there only because the function requires it.\r\nif __name__ == '__main__':\r\n for num in fibonacci_generator(previous=0, current=1, maximum=101):\r\n pass</code></pre>\r\n</div>\r\n</div>\r\n<p><strong><u>Solution 3</u>: Use a recursive function with the lru_cache decorator from functools to implement memoization</strong></p>\r\n<p>This implementation is perhaps the most interesting of them all because it gives an insight into recursive functions and memoization.</p>\r\n<div>\r\n<div>\r\n<pre><code class=\"language-python\"># The code below will not work as intended.\r\n# It will grind to a halt as the call stack keeps increasing.\r\ndef fibonacci(n):\r\n if n == 0:\r\n return 0\r\n elif n == 1:\r\n return 1\r\n elif n > 1:\r\n return fibonacci(n - 1) + fibonacci(n - 2)\r\n\r\nif __name__ == \"__main__\":\r\n for n in range(0, 101):\r\n print('Fib', n, '=', fibonacci(n))</code></pre>\r\n</div>\r\n</div>\r\n<p>However, after importing <strong>'lru_cache'</strong> function from the <strong>functools</strong> built-in module and applying it as a decorator to the <strong>fibonacci_recursive</strong> function, then it will work as the implemented <em>memoization</em> means that the result from each run is cached and carried forward into the next recursive function call. This prevents the call stack from getting larger on each call like it was in the function above.</p>\r\n<div>\r\n<div>\r\n<pre><code class=\"language-python\">from functools import lru_cache # Added\r\n\r\n@lru_cache(maxsize=1000) # Added\r\ndef fibonacci_recursive(num):\r\n if num == 0:\r\n return 0\r\n elif num == 1:\r\n return 1\r\n elif num > 1:\r\n return fibonacci_recursive(num - 1) + fibonacci_recursive(num - 2)\r\n\r\nif __name__ == \"__main__\":\r\n for num in range(0, 101):\r\n print('Fib', num, '=', fibonacci_recursive(num))</code></pre>\r\n</div>\r\n</div>\r\n<p>Finally, here is solution 1 in the equivalent JavaScript.</p>\r\n<div>\r\n<div>\r\n<pre><code class=\"language-javascript\">function fibonacciArray(previous, current, maximum) {\r\n fibNums = []\r\n for (let num = 1; num < maximum; num++) {\r\n fibNums.push(previous)\r\n console.log(`Fib ${num} = ${previous}`)\r\n current = [previous + current, previous = current][0];\r\n }\r\n return fibNums\r\n}\r\n\r\n\r\nfibonacciArray(previous = 0,current = 1,maximum = 101)</code></pre>\r\n</div>\r\n</div>", "reference_url": "", "publish_date": "2019-09-02T16:19:00.201000Z", "updated_date": "2020-10-04T14:09:34.132898Z", "image": "https://wl-portfolio.s3.amazonaws.com/post_images/algorithm5.jpg", "status": "Publish", "word_count": 469, "reading_time": 7, "post_absolute_url": "/blog/post/fibonacci-sequence-algorithm-python/", "author_username": "wayne-lambert", "author_first_name": "Wayne", "author_last_name": "Lambert", "author_full_name": "Wayne Lambert", "author_initials": "WL", "author_display_name": "Wayne Lambert", "author_join_year": 2019, "author_view": 1, "author_created_date": "2019-07-21T10:02:09.888000Z", "author_updated_date": "2022-12-14T14:44:36.068604Z", "author_absolute_url": "/blog/users/wayne-lambert/profile/", "author_profile_picture": "https://wl-portfolio.s3.amazonaws.com/profile_pics/gravatar-500.jpg", "categories": [ { "id": 8, "name": "Algorithms", "slug": "algorithms", "created_date": "2019-09-05T08:51:56.026246Z" }, { "id": 10, "name": "Data Structures", "slug": "data-structures", "created_date": "2019-09-26T20:04:59.582866Z" }, { "id": 4, "name": "JavaScript", "slug": "javascript", "created_date": "2019-07-27T21:39:47.440429Z" }, { "id": 9, "name": "Python", "slug": "python", "created_date": "2019-09-05T09:25:47.465274Z" } ] }, { "id": 18, "title": "Finding the Largest or Smallest n Items in Python", "slug": "finding-largest-smallest-n-items-in-python", "content": "<p>Create a list that contains either the smallest or largest n number of items. This can be done with either a list or a dictionary.</p>\r\n<p>The <strong>heapq</strong> module has some functions that will help to do this: <strong>nlargest()</strong> and <strong>nsmallest()</strong></p>\r\n<p>This makes creating a list of these extremely easy.</p>\r\n<pre><code class=\"language-python\">import heapq\r\n\r\n# Create a list of square numbers between 1 and 10\r\nsquare_nums = [num ** 2 for num in range(1, 11)]\r\n\r\n# List of the 3 smallest items - ascending order\r\nprint(heapq.nsmallest(3, square_nums))\r\n\r\n# List of the 3 largest items - ascending order\r\nprint(heapq.nlargest(3, square_nums)[::-1])\r\n\r\n# List of the 3 largest items - descending order\r\nprint(heapq.nlargest(3, square_nums))\r\n\r\n# Create a list of even numbers between 1 and 100\r\neven_nums = [num for num in range(1, 101) if num % 2 == 0]\r\n\r\n# List of the first 10 even numbers between 1-100 - ascending order\r\nprint(heapq.nsmallest(10, even_nums))\r\n\r\n# Create a list of numbers between 1 and 1000 that are a multiple of 7\r\nmultiples_of_seven = [num for num in range(1, 1001) if num % 7 == 0]\r\n\r\n# List of the last 10 multiple of 7 between 1-1000 - ascending order\r\nprint(heapq.nlargest(10, multiples_of_seven)[::-1])\r\n</code></pre>\r\n<p>Key parameters can also be used within the <strong>nlargest()</strong> and <strong>nsmallest() </strong>functions which means that more complicated data structures like a dictionary can be used. A lambda function is used to access the value from the price key for the 2 cheapest and 2 most expensive share prices.</p>\r\n<pre><code class=\"language-python\">portfolio = [\r\n {'name': 'GOOGL', 'shares': 100, 'price': 1242.29},\r\n {'name': 'AMZN', 'shares': 35, 'price': 1739.84},\r\n {'name': 'TSLA', 'shares': 75, 'price': 242.56},\r\n {'name': 'AAPL', 'shares': 50, 'price': 219.89},\r\n {'name': 'FB', 'shares': 200, 'price': 180.09},\r\n {'name': 'MSFT', 'shares': 45, 'price': 139.54},\r\n]\r\n\r\ncheap = heapq.nsmallest(2, portfolio, key=lambda s: s['price'])\r\nexpensive = heapq.nlargest(2, portfolio, key=lambda s: s['price'])\r\n\r\nprint(f\"Cheap Shares\\n '{cheap}\")\r\nprint(f\"Expensive Shares\\n '{expensive}\")</code></pre>", "reference_url": "", "publish_date": "2019-09-26T20:48:56.662759Z", "updated_date": "2020-10-04T14:07:49.982019Z", "image": "https://wl-portfolio.s3.amazonaws.com/post_images/algorithm7.jpg", "status": "Publish", "word_count": 305, "reading_time": 5, "post_absolute_url": "/blog/post/finding-largest-smallest-n-items-in-python/", "author_username": "wayne-lambert", "author_first_name": "Wayne", "author_last_name": "Lambert", "author_full_name": "Wayne Lambert", "author_initials": "WL", "author_display_name": "Wayne Lambert", "author_join_year": 2019, "author_view": 1, "author_created_date": "2019-07-21T10:02:09.888000Z", "author_updated_date": "2022-12-14T14:44:36.068604Z", "author_absolute_url": "/blog/users/wayne-lambert/profile/", "author_profile_picture": "https://wl-portfolio.s3.amazonaws.com/profile_pics/gravatar-500.jpg", "categories": [ { "id": 9, "name": "Python", "slug": "python", "created_date": "2019-09-05T09:25:47.465274Z" } ] } ] }{ "count": 17, "next": "