<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ricardo</title>
    <description>The latest articles on DEV Community by Ricardo (@makiolo).</description>
    <link>https://dev.to/makiolo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F570073%2F34f0ac60-e740-4a33-8776-2f8f885833a6.png</url>
      <title>DEV Community: Ricardo</title>
      <link>https://dev.to/makiolo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/makiolo"/>
    <language>en</language>
    <item>
      <title>Django model + Frontend CRUD + pagination + API REST and filters by Example</title>
      <dc:creator>Ricardo</dc:creator>
      <pubDate>Sat, 30 Jan 2021 21:02:00 +0000</pubDate>
      <link>https://dev.to/makiolo/django-model-frontend-crud-pagination-api-rest-filters-by-example-48d3</link>
      <guid>https://dev.to/makiolo/django-model-frontend-crud-pagination-api-rest-filters-by-example-48d3</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create model&lt;/li&gt;
&lt;li&gt;Create frontend&lt;/li&gt;
&lt;li&gt;Create API&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create model &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We type in base dir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py startapp stocks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register app in settings.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSTALLED_APPS = [
    ...
    'stocks',
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We write initial model in "models.py":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models

class Stock(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=128)
    price = models.FloatField()
    created = models.DateTimeField(auto_now_add=True)
    update = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "stock"
        verbose_name_plural = "stocks"
        ordering = ['-id', ]

    def __str__(self):
        return self.name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply migrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your ModelAdmin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib import admin
from django.contrib.admin import register
from stocks.models import Stock


@register(Stock)
class StockAdmin(admin.ModelAdmin):
    list_display = ['id', 'name', 'price', 'update']

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you should be see something like this in your django admin:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl7b1bscn89mqrvraebzl.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl7b1bscn89mqrvraebzl.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create frontend &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Create a ModelForm for after render it. Create new file "forms.py".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django import forms
from .models import Stock

class StockForm(forms.ModelForm):
    class Meta:
        model = Stock
        fields = ['name', 'price']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we should create a CRUD views in file "views.py" (note , i add staff authentication required in update, create and delete but not is list amd not in detail):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Stock
from .forms import StockForm
from django.utils.decorators import method_decorator
from django.contrib.admin.views.decorators import staff_member_required


class StockListView(ListView):
    model = Stock
    paginate_by = 2  # change it, is page size

    def get_queryset(self):
        query = self.request.GET.get('q')
        if query:
            object_list = self.model.objects.filter(name__icontains=query)
        else:
            object_list = self.model.objects.all()
        return object_list


class StockDetailView(DetailView):
    model = Stock


@method_decorator(staff_member_required, name='dispatch')
class StockCreate(CreateView):
    model = Stock
    form_class = StockForm
    template_name_suffix = '_create_form'
    success_url = reverse_lazy('stocks:list')


@method_decorator(staff_member_required, name='dispatch')
class StockUpdate(UpdateView):
    model = Stock
    form_class = StockForm
    template_name_suffix = '_update_form'

    def get_success_url(self):
        return reverse_lazy('stocks:update', args=(self.object.id, )) + '?ok'


@method_decorator(staff_member_required, name='dispatch')
class StockDelete(DeleteView):
    model = Stock
    success_url = reverse_lazy('stocks:list')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We connect this views to url paths (create "urls.py"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.urls import path
from .views import StockListView, StockDetailView, StockCreate, StockUpdate, StockDelete


stocks_patterns = ([
    path('', StockListView.as_view(), name='list'),
    path('&amp;lt;int:pk&amp;gt;/&amp;lt;slug:slug&amp;gt;/', StockDetailView.as_view(), name='detail'),
    path('create/', StockCreate.as_view(), name='create'),
    path('update/&amp;lt;int:pk&amp;gt;', StockUpdate.as_view(), name='update'),
    path('delete/&amp;lt;int:pk&amp;gt;', StockDelete.as_view(), name='delete'),
], 'stocks')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In settings.urls we add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
from stocks.urls import stocks_patterns
...

urlpatterns = [
    ...
    path('stocks/', include(stocks_patterns)),
    ...
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This CRUD views need next 5 templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stocks/templates/stocks/stock_list.html&lt;/li&gt;
&lt;li&gt;stocks/templates/stocks/stock_detail.html&lt;/li&gt;
&lt;li&gt;stocks/templates/stocks/stock_create_form.html (custom prefix)&lt;/li&gt;
&lt;li&gt;stocks/templates/stocks/stock_update_form.html (custom prefix)&lt;/li&gt;
&lt;li&gt;stocks/templates/stocks/stock_confirm_delete.html&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;stocks/templates/stocks/stock_list.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'core/base.html' %}
{% load static %}

{% block title %}Stocks{% endblock %}
{% block content %}
    &amp;lt;form method='GET'&amp;gt;
      &amp;lt;div class="columns is-centered mb-1"&amp;gt;
        &amp;lt;div class="column is-11"&amp;gt;
            &amp;lt;p class="control has-icons-left"&amp;gt;
              &amp;lt;input class='input' type='text' name='q' value='{{ request.GET.q }}' placeholder="Search"&amp;gt;
              &amp;lt;span class="icon is-left"&amp;gt;
                &amp;lt;i class="fas fa-search" aria-hidden="true"&amp;gt;&amp;lt;/i&amp;gt;
              &amp;lt;/span&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;div class="column"&amp;gt;
          &amp;lt;input class="button is-primary" type='submit' value="Search"&amp;gt;
        &amp;lt;/div&amp;gt;

      &amp;lt;/div&amp;gt;

    &amp;lt;/form&amp;gt;

    {% if stock_list|length == 0 %}
        No se han encontrado resultados.
        &amp;lt;a href="{% url 'stocks:create' %}" class="card-footer-item"&amp;gt;Create&amp;lt;/a&amp;gt;
    {% endif %}

    {% for stock in stock_list %}

      &amp;lt;div class="card"&amp;gt;
        &amp;lt;header class="card-header"&amp;gt;
          &amp;lt;p class="card-header-title"&amp;gt;
            {{ stock.name }}
          &amp;lt;/p&amp;gt;
          &amp;lt;a href="#" class="card-header-icon" aria-label="more options"&amp;gt;
            &amp;lt;span class="icon"&amp;gt;
              &amp;lt;i class="fas fa-angle-down" aria-hidden="true"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;/span&amp;gt;
          &amp;lt;/a&amp;gt;
        &amp;lt;/header&amp;gt;
        &amp;lt;div class="card-content"&amp;gt;
          &amp;lt;div class="content"&amp;gt;
            {{stock.name|striptags|safe|truncatechars:"200"}} {{stock.price}}
            &amp;lt;br /&amp;gt;
            &amp;lt;time datetime="2016-1-1"&amp;gt;{{stock.updated}}&amp;lt;/time&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;footer class="card-footer"&amp;gt;

          {% if request.user.is_staff %}
            &amp;lt;a href="{% url 'stocks:create' %}" class="card-footer-item"&amp;gt;Create&amp;lt;/a&amp;gt;
            &amp;lt;a href="{% url 'stocks:delete' stock.id %}" class="card-footer-item"&amp;gt;Delete&amp;lt;/a&amp;gt;
            &amp;lt;a href="{% url 'stocks:update' stock.id %}" class="card-footer-item"&amp;gt;Edit&amp;lt;/a&amp;gt;
          {% endif %}
          &amp;lt;a href="{% url 'stocks:detail' stock.id stock.name|slugify %}" class="card-footer-item"&amp;gt;Read more ...&amp;lt;/a&amp;gt;
        &amp;lt;/footer&amp;gt;
      &amp;lt;/div&amp;gt;

    &amp;lt;br /&amp;gt;

    {% endfor %}



    {% if stock_obj.paginator.num_pages &amp;gt; 1 %}
    &amp;lt;nav class="pagination" role="navigation" aria-label="pagination"&amp;gt;
      {% if stock_obj.has_previous %}
          &amp;lt;a class="pagination-previous" href="?page={{ stock_obj.previous_page_number }}&amp;amp;q={{ request.GET.q }}"&amp;gt;Previous&amp;lt;/a&amp;gt;
      {% endif %}
      {% if stock_obj.has_next %}
          &amp;lt;a class="pagination-next" href="?page={{ stock_obj.next_page_number }}&amp;amp;q={{ request.GET.q }}"&amp;gt;Next page&amp;lt;/a&amp;gt;
      {% endif %}
      &amp;lt;ul class="pagination-list"&amp;gt;
        {% if stock_obj.number &amp;gt; 3 %}
          &amp;lt;li&amp;gt;
            &amp;lt;a class="pagination-link" aria-label="Goto page 1" href="?page=1&amp;amp;q={{ request.GET.q }}"&amp;gt;1&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          {% if stock_obj.number &amp;gt; 4 %}
            &amp;lt;li&amp;gt;
              &amp;lt;span class="pagination-ellipsis"&amp;gt;&amp;amp;hellip;&amp;lt;/span&amp;gt;
            &amp;lt;/li&amp;gt;
          {% endif %}
        {% endif %}

        {% for i in stock_obj.paginator.page_range %}
        &amp;lt;li&amp;gt;
          {% with leftmax=stock_obj.number|add:"-3" %}
            {% with rightmax=stock_obj.number|add:"+3" %}
              {% if leftmax &amp;lt; i %}
                {% if i &amp;lt; rightmax %}

                  {% if i == stock_obj.number %}
                    &amp;lt;a class="pagination-link is-current" aria-label="Goto page {{ i }}" href="?page={{ i }}&amp;amp;q={{ request.GET.q }}" aria-current="page"&amp;gt;{{ i }}&amp;lt;/a&amp;gt;
                  {% else %}
                    &amp;lt;a class="pagination-link" aria-label="Goto page {{ i }}" href="?page={{ i }}&amp;amp;q={{ request.GET.q }}"&amp;gt;{{ i }}&amp;lt;/a&amp;gt;
                  {% endif %}

                {% endif %}
              {% endif %}
            {% endwith %}
          {% endwith %}
        &amp;lt;/li&amp;gt;
        {% endfor %}

        {% with rightdistance=stock_obj.paginator.num_pages|add:"-2" %}
          {% with rightdistanceplus=stock_obj.paginator.num_pages|add:"-3" %}
            {% if stock_obj.number &amp;lt; rightdistance %}
              {% if stock_obj.number &amp;lt; rightdistanceplus %}
                &amp;lt;li&amp;gt;
                  &amp;lt;span class="pagination-ellipsis"&amp;gt;&amp;amp;hellip;&amp;lt;/span&amp;gt;
                &amp;lt;/li&amp;gt;
              {% endif %}
              &amp;lt;li&amp;gt;
                &amp;lt;a class="pagination-link" aria-label="Goto page {{ stock_obj.paginator.num_pages }}" href="?page={{ stock_obj.paginator.num_pages }}&amp;amp;q={{ request.GET.q }}"&amp;gt;{{ stock_obj.paginator.num_pages }}&amp;lt;/a&amp;gt;
              &amp;lt;/li&amp;gt;
            {% endif %}
          {% endwith %}
        {% endwith %}

      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    {% endif %}

{% endblock %}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;stocks/templates/stocks/stock_detail.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'core/base.html' %}
{% load static %}
{% block title %}{{object.name}}{% endblock %}
{% block content %}
&amp;lt;main role="main"&amp;gt;
  &amp;lt;div class="container"&amp;gt;
    &amp;lt;div class="row mt-3"&amp;gt;
      &amp;lt;div class="col-md-9 mx-auto"&amp;gt;
        &amp;lt;h2 class="title"&amp;gt;{{object.name}}&amp;lt;/h2&amp;gt;
        &amp;lt;div&amp;gt;
          name: {{object.name|safe}}&amp;lt;br /&amp;gt;
          price: {{object.price}}&amp;lt;br /&amp;gt;
          {% if request.user.is_staff %}
          &amp;lt;br /&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;a href="{% url 'stocks:list' %}"&amp;gt;Volver&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;a href="{% url 'stocks:update' object.id %}"&amp;gt;Editar&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
          {% endif %}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/main&amp;gt;
{% endblock %}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;stocks/templates/stocks/stock_create_form.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'core/base.html' %}
{% load bulma_tags %}
{% load static %}

{% block title %}Create{% endblock title %}

{% block content %}
&amp;lt;form method="post"&amp;gt;
   {% csrf_token %}
   {{ form | bulma }}
   &amp;lt;div class="field"&amp;gt;
     &amp;lt;button type="submit" class="button is-primary"&amp;gt;Create&amp;lt;/button&amp;gt;
   &amp;lt;/div&amp;gt;
   &amp;lt;input type="hidden" name="next" value="{{ next }}"/&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href="{% url 'stocks:list' %}"&amp;gt;Volver&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
{% endblock content %}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;stocks/templates/stocks/stock_update_form.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'core/base.html' %}
{% load bulma_tags %}
{% load static %}

{% block title %}Update{% endblock title %}

{% block content %}
{% if 'ok' in request.GET %}
    &amp;lt;article class="message is-primary"&amp;gt;
      &amp;lt;div class="message-header"&amp;gt;
        &amp;lt;p&amp;gt;Actualizado&amp;lt;/p&amp;gt;
        &amp;lt;button class="delete" aria-label="delete"&amp;gt;&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="message-body"&amp;gt;
          Updated. &amp;lt;a href="{% url 'stocks:detail' stock.id stock.name|slugify %}"&amp;gt;more details here.&amp;lt;/a&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/article&amp;gt;
{% endif %}
&amp;lt;form method="post"&amp;gt;
   {% csrf_token %}
   {{ form|bulma }}
   &amp;lt;div class="field"&amp;gt;
     &amp;lt;button type="submit" class="button is-primary"&amp;gt;Update&amp;lt;/button&amp;gt;
   &amp;lt;/div&amp;gt;
   &amp;lt;input type="hidden" name="next" value="{{ next }}"/&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href="{% url 'stocks:list' %}"&amp;gt;Volver&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
{% endblock content %}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;stocks/templates/stocks/stock_confirm_delete.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'core/base.html' %}
{% load static %}
{% block title %}{{object.name}}{% endblock %}
{% block content %}
&amp;lt;div class="box has-text-centered"&amp;gt;
  &amp;lt;form action="" method="post"&amp;gt;{% csrf_token %}
      &amp;lt;p class="mb-3"&amp;gt;¿Estás seguro de que quieres borrar &amp;lt;b&amp;gt;"{{ object }}"&amp;lt;/b&amp;gt;?&amp;lt;/p&amp;gt;
      &amp;lt;input class="button is-primary" type="submit" value="Sí, borrar la página" /&amp;gt;
      &amp;lt;input class="button is-danger" onclick="history.go(-1); return false;" value="Cancelar" /&amp;gt;
  &amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use 'core/base.html' from django-bulma. Here the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp9ijpub4rz45d40exza8.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp9ijpub4rz45d40exza8.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have search, pagination, edit form, create form and delete. All this, with minimal code and very customizable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create API &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;For expose this model to API REST, we use django restframework and django-filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pip install djangorestframework
$ pip install django-filter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And register it in settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSTALLED_APPS = [
    ...
    'rest_framework',
    'django_filters',
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For convert model to json, or json to model we need create "serializers.py" file, and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework import serializers
from .models import Stock


class StockSerializer(serializers.ModelSerializer):
    class Meta:
        model = Stock
        fields = '__all__'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For create a API Rest that let "filter" and "order" for someone field of model. We create "filters.py" and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import django_filters
from .models import Stock


class StockFilter(django_filters.FilterSet):
    id = django_filters.NumberFilter()
    name = django_filters.CharFilter(lookup_expr='iexact')
    price = django_filters.CharFilter(lookup_expr='eq')
    order = django_filters.OrderingFilter(fields=('name', 'price', ))

    class Meta:
        model = Stock
        fields = '__all__'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we need create a API View using your serializer and your filter. We create "viewsets.py" and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework import viewsets
from .models import Stock
from .serializers import StockSerializer
from .filters import StockFilter


class StockViewSet(viewsets.ModelViewSet):
    queryset = Stock.objects.all()
    serializer_class = StockSerializer
    filterset_class = StockFilter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are registering this viewset in settings.router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework import routers
from stocks.viewsets import StockViewSet

router = routers.DefaultRouter()
router.register('stocks', StockViewSet)

urlpatterns = [
    ...
    path('api/v1/', include(router.urls)),
    ...
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maybe you need a good config for restframework.auth. Using JWT, Cookies, or whatever you want.&lt;/p&gt;

&lt;p&gt;The API (is full CRUD + filters) is available in: &lt;a href="http://localhost:8000/api/v1/stocks/" rel="noopener noreferrer"&gt;http://localhost:8000/api/v1/stocks/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recommend "drf-spectacular" for generate API documentation in swagger, or redoc format.&lt;/p&gt;

&lt;p&gt;I hope this is useful for someone.&lt;/p&gt;

&lt;p&gt;Greats. Ricardo.&lt;br&gt;
Follow me for more content.&lt;br&gt;
&lt;a href="https://dev.to/makiolo"&gt;&lt;br&gt;
  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2fltix0v2e0sb.cloudfront.net%2Fdev-badge.svg" alt="Ricardo's DEV Community Profile"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>crud</category>
      <category>pagination</category>
      <category>api</category>
    </item>
  </channel>
</rss>
