Adding additional features
As mentioned, this app is designed to demonstrate some common use cases, but there are likely to be many more things that you want to change about it. I won't cover all of them here, but provide some common locations to find things you might want to add.
New tables
The tables are built using the Django models.py
files. You can add new tables inside these using the standard Django definitions. New tables that are going to form part of the main database should be in app/api/models.py
, but any changes required to the user profile can be found in app/accounts/models.py
.
Once you have defined or made changes to the models, you can create the database tables by running:
1 2 |
|
New profile fields
If you are wanting to capture additional user data, you should do so using the User Profile. This can be found in app/accounts/models.py
. Once the models have been updated, you will need to also update the RegisterAPI
class in app/accounts/api.py
to ensure that these are captured:
1 2 3 4 5 6 7 8 9 10 11 |
|
Adding fields to the admin section
There are some basic set up in the admin section included here, but it's highly likely you'll want to add additional settings. I won't include how to register tables with admin, as this is well covered in the Django documentation, but instead include helpful hints.
Tabular Paginated Inlines
When working with data models that have a large amount of related fields, it can take a very long time to load all of the related data. Instead, you can make use of the django-admin-inline-paginator
to paginate the inlines.
The basic structure, is to install it using pip install django-admin-inline-paginator
(remember to also add it to your requirements file):
1 2 3 4 |
|
Regenerate your requirements.txt
using pip-compile
. Then, add it to your settings as an installed app:
1 2 3 4 5 6 7 |
|
Then in your admin.py
file where you create and register your models, import it and create a subclass, then add it as an inline in the parent admin model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
List view fields
The list view (i.e. what you see when you first access the admin panel for that Model) allows any fields that are defined on the model. This includes the string representation __str__
, as well as calculated fields defined on the model set, and custom fields defined in the ModelAdmin.
When using related fields, it's good to include, list_prefetch_related
to speed up the table.
Here is an advanced example of how these can be combined:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Search and Filter on related fields
As the search and filter options allow you to use any string representation of a field name, you can also include related fields as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Readonly and revoke delete permissions
You can set readonly fields within the admin panel by using the readonly_fields
attribute.
If you want to make something editable only only creation, then you need to use the get_readonly_fields
method. Additionally, if you want to prevent the user from being able to delete it, then you need to use the has_delete_permission
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
New API endpoints
Backend API creation
When you have new data, it is likely that you will want to create ways of accessing this by exposing them via API endpoints. The easiest way to do this, is to create a ModelSerializer
in app/api/serializers.py
then use the ModelViewSet
to automatically create all required REST API endpoints. You can see an example of how this can be used, and integrated with pagination, filter and search functionality in the example provided in app/api/api.py
.
Once you have this defined, you need to register it with your router in app/api/urls.py
:
1 2 3 4 5 6 7 8 9 10 |
|
If you don't need all endpoints (for example, perhaps you only want to create a read-only endpoint), then instead, you want to use a View Class. You will still need to create the serializer first.
You can see an example of making use of Generic Views in the app/accounts/api.py
file, as they are used as part of the user management process.
The biggest difference when using them, is that you don't register them with the router, but instead provide them as paths in your app/api/urls.py
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
When creating the views, it's worth using prefetch_related
and select_related
to speed up the collection of related data. You can read about the detail of this here. Broadly though:
In Django, select_related
and prefetch_related
are designed to stop the deluge of database queries that are caused by accessing related objects.
select_related()
“follows” foreign-key relationships, selecting additional related-object data when it executes its query.prefetch_related()
does a separate lookup for each relationship, and does the “joining” in Python.
One uses select_related
when the object that you’re going to be selecting is a single object, so OneToOneField
or a ForeignKey
. You use prefetch_related
when you’re going to get a “set” of things, so ManyToManyFields
or reverse ForeignKeys.
Frontend API pickup
Once you have created (and tested) your API endpoints, you can then integrate them into the frontend app. To do this, you need to identify whether this is a new feature of the app, or it relates to something that already exists.
If it's a new feature, you should add a new folder inside app/frontend/src/features
, if not, you can add the endpoint to the relevant Slice. For API calls, as the base route for all is the same, there is a single CreateAPI
slice created in app/frontend/src/app/splitApiSlice.js
, and endpoints are injected into it from the individual feature slices.
Here is an example of a full CRUD API set up, including how tags can be added and validated, and success messages can be sent. Broadly speaking, queries
are used when the database isn't changed, whereas mutations
involve a change in the underlying data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
|