create-model-and-api.mo
Owner: Alice Breton
Prerequisites (~45 mins)
Have a django project installed with an app created
In this example we will use an already existing model
city
in alocations
app, to show how to integrate othermodels with foreign keys.
Steps
In this tutorial we will create a
News
model, add it to the Django admin back-office and create a GET API route toretrieve all the news that have been created.
Create a News model (~10 min)
In this example we will create a new model called
News
that has different attributes as you will see bellow. In themodels.py file of your app (in our case
Publications
) create a new model.
# File: "our_django_project/publications/models.py"
from django.db import models
class News(models.Model):
title = models.CharField(max_length=50)
description = models.TextField(max_length=1000)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
city = models.ForeignKey('locations.city')
class Meta:
verbose_name_plural = "News"
def __str__(self):
return self.title
CharField
will return a single line form in the admin; max_length speaks for itself.TextField
will return a scrollable paragraph in the admin.auto_now_add
generates automatically a timestamp when a news is created.auto_now
updates the timestamp every time you change the news.Concerning the city attribute which is a many to one relationship: every piece of news is linked to one city, two pieces of news can have the same city.
It's standard that the name of a model (not class) is singular, and django automatically puts it to plural for external use. However here, News is a singular/plural english name, so we need to make sure Django does not add a second 's' (Newss) by overriding
verbose_name_plural
Finally, make your migrations and migrate. To do this run the following commands in your shell:
python3 manage.py makemigrations
python3 manage.py migrate
or adapt the previous commands, if you run your project with docker.
Note: Here is an article that goes into more detail How to Create Django Data Migrations
What you can check!
python3 manage.py showmigrations
python3 manage.py dbshell
In the shell display the table (in case of psql
\d
) and check the existence of your model. More info here
Add permissions to a group (~10 min)
Users that are in groups need permission to access the Model. If you don't have a group yet, you can create one by
clicking on group on the admin web site. We can now add permissions to them that will be automatically added when
you deploy.
# File: "our_django_project/users/apps.py"
from django.apps import AppConfig
from django.contrib.admin import site
from django.db.models.signals import post_migrate
# This function takes a group and a model and adds all the permissions (read, update, delete) of this model to the group
def add_model_permissions(group, model, ContentType, Permission):
content_type = ContentType.objects.get_for_model(model)
permissions = Permission.objects.filter(content_type=content_type)
for permission in permissions:
group.permissions.add(permission)
You can than create a migration to add the groups permissions when the migrations are run
Create an empty migration:
python manage.py makemigrations --empty users
Then fill in your migration like so:
from django.db import migrations
# This function takes the model News and adds the permissions to the group Mayor Admin
def add_group_permissions(sender, using, apps, **kwargs):
Group = apps.get_model("auth", "Group")
Permission = apps.get_model("auth", "Permission")
ContentType = apps.get_model("contenttypes", "ContentType")
News = apps.get_model("publications", "News") #Add this line
#If the group Mayor Admin doesn't exist, this piece of code creates it.
if Group.objects.using(using).filter(name='Mayor Admin').exists():
group = Group.objects.get(name='Mayor Admin')
else:
group = Group.objects.using(using).create(name='Mayor Admin')
add_model_permissions(group, News, ContentType, Permission) #Add this second line
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.RunPython(add_group_permissions),
]
Then run
python manage.py migrate
What you can check!
Add the News model to the admin (~5 min)
We now need to add the model to the admin back-office.
# File: "our_django_project/publications/admin.py"
from django.contrib import admin
from .models import News
@admin.register(News)
class MyNewsAdmin(admin.ModelAdmin):
list_display = (
'title',
'city'
)
If you don't add the list_display
property, the default column title in the admin will be the result of the magic method str returned in the model (you can read more here). list_display
allows you to add several columns to the admin interface.
Without list_display
:
With list_display
:
What you can check!
Note: you can learn how to add other configuration to your admin by reading the official documentation
Serialize the News you get from the database (~5 min)
Serializers translates Django models into other formats(json, xml). In our project want to get a JSON response. Check out the official Django doc for more info.
# File: "our_django_project/publications/serializers.py"
from .models import News
from rest_framework import serializers
class NewsSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(source='pk')
city = serializers.StringRelatedField(many=False, source="city.pk") #We need this to get the primary key of the city that is attached to this news
class Meta:
model = News
fields = ('title', 'description', 'created_at', 'updated_at', 'city', 'id') #All the fields you wish to get
Create a News ViewSet (~5 min)
ViewSets will allow you to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically
Note: Check out the Django doc for more info ViewSets
# File: "our_django_project/publications/viewsets.py"
from .models import News
from rest_framework import viewsets
from .serializers import NewsSerializer
#This is an example of filtering the pieces of news with the city-id like this: /?city-id=5
class FilterByCity(object):
def get_queryset(self): #You need to override the default get_queryset method
queryset = super().get_queryset()
city_id = self.request.query_params.get('city-id')
# This filters the queryset by city (if there is a city)
if city_id is not None:
queryset = queryset.filter(city=city_id)
return queryset
class NewsViewSet(FilterByCity, viewsets.ModelViewSet):
queryset = News.objects.all()
serializer_class = NewsSerializer
The NewsViewSet class will now inherit both classes FilterByCity viewsets.ModelViewSet from right to left. Therefore, the FilterByCity get_queryset method will override the ModelViewSet one.
Note: You can also use automatically generated filters (have a look here)
Mount the News
ViewSet to an endpoint using a router (~5 min)
News
ViewSet to an endpoint using a router (~5 min)Django routers will allow you to easily generate different routes from your ViewSet (GET, POST, ...)
# File: "our_django_project/config/router.py"
from rest_framework import routers
from our_django_project.users.viewsets import UserViewSet
from our_django_project.locations.viewsets import CityViewSet
from our_django_project.publications.viewsets import NewsViewSet #Add this line
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'locations/cities', CityViewSet)
router.register(r'publications/news', NewsViewSet) #Add this one too
Note:Here is the Django doc if you want to learn more about Django routers
What you can check!
[
{
"title": "Reading the article",
"description": "Hello, I am glad I read this article!",
"created_at": "2017-11-08T15:31:30.597524Z",
"updated_at": "2017-11-08T15:31:30.597555Z",
"city": "66",
"id": 2
},
{
"title": "Tutorial",
"description": "I followed the tutorial to create a new model",
"created_at": "2017-11-08T15:33:28.834694Z",
"updated_at": "2017-11-08T15:33:28.834721Z",
"city": "66",
"id": 3
},
{
"title": "Les Français adorent !",
"description": "J'ai lu cet article, il m'a aidé à faire mon premier modèle, youhouu !",
"created_at": "2017-11-08T15:20:33.457740Z",
"updated_at": "2017-11-08T16:54:19.322703Z",
"city": "5",
"id": 1
}
]
[
{
"title": "Les Français adorent !",
"description": "Je suis française et j'ai lu cet article, il m'a aidé à faire mon premier modèle, youhouu !",
"created_at": "2017-11-08T15:20:33.457740Z",
"updated_at": "2017-11-08T16:54:19.322703Z",
"city": "5",
"id": 1
}
]
Last updated