DynamicNestedField is a set of tools used to perform dynamic nested operation on django models.
Project description
DynamicNestedField
DynamicNestedField
is a set of tools used to perform dynamic nested operation on django models without worrying about the problems and authentication leaks that come with it.
Installation
Install using pip
...
$ pip install DynamicNestedField
Usage & Example
Working with this library is semilunar to using normal serializers, we will create simple project that contains several models with m2m and foreignkey relations...
👉 You can find a simple example project on GitHub.
- model:
A
- ManyToMany: model:
B
- ForeignKey: model:
C
- ForeignKey: model:
- ManyToMany: model:
models:
Here we will define three models as following...
from django.db import models
class C(models.Model):
charfield = models.CharField(max_length=100)
class B(models.Model):
charfield = models.CharField(max_length=100)
c = models.ForeignKey(C, on_delete=models.CASCADE, null=True, blank=True)
class A(models.Model):
charfield = models.CharField(max_length=100)
b = models.ManyToManyField(B)
Serializers:
And this is the main serializers that we are using to perform all models operations, we will talk about it just in seconds.
class C_Serializer(DynamicNestedMixin):
class Meta:
model = C
fields = ['charfield']
permission_classes = [IsAuthenticated] # the general permission class.
permission_classes_by_method = {
'GET': [IsAuthenticated],
'POST': [IsAuthenticated],
'PUT': [IsAuthenticated],
'DELETE': [IsAuthenticated],
# and so on.
}
class B_Serializer(DynamicNestedMixin):
c = C_Serializer()
class Meta:
model = B
fields = ['charfield', 'c']
DNM_config = { # DNM_config holds all the configuration needed.
"c": {
"filter": "id",
}
}
permission_classes = [IsAuthenticated] # the general permission class.
permission_classes_by_method = {
'GET': [IsAuthenticated],
'POST': [IsAuthenticated],
'PUT': [IsAuthenticated],
'DELETE': [IsAuthenticated],
# and so on.
}
class A_Serializer(DynamicNestedMixin):
b = B_Serializer(many=True) # many=True for m2m.
class Meta:
model = A
fields = ['charfield', 'b']
DNM_config = { # DNM_config holds all the configuration needed.
"b": {
"filter": "id",
}
}
permission_classes = [IsAuthenticated] # the general permission class.
permission_classes_by_method = {
'GET': [IsAuthenticated],
'POST': [IsAuthenticated],
'PUT': [IsAuthenticated],
'DELETE': [IsAuthenticated],
# and so on.
}
As you can see we define our serializers as usual, except this time we use three new variables in our Meta class, the first one is permission_classes
this variable is an instance of djangoRestFramework VewSet class permission_classes variable it does the same thing, takes a list of BasePermission
classes that can be used to define permissions by using predefined permissions classes or by creating your own.
The second Variable is permission_classes_by_method
this is the same as the previous permission_classes
but here we can define a dict var with its keys as request methods (POST, PUT, GET ...) so we can set custom permissions for each one, if you didn't specify a request method here then the library will use the default permissions that are located in permission_classes
.
Last variable and the most important one is DNM_config
, here we define all serializer fields configuration
The default options we have in DNM_config
are as following...
DNM_config = {
"field": { # Default Values...
"create_new_instance": True, # if you want to perform create operation on this field.
"can_be_edited": True, # if you want to perform update operation on this field.
"clear_data": False, # if you want to clear field data before updating it (like if it was m2m relation, and you want to clear the data every time you update using this serializer).
"filter": [None], # the filter field used to get old data of this field from the database, if the first filter was not found then it will check for the secondary if exists (this attribute must be defined).
"serializer": None # you can set a serializer for this field the library will search for it by itself.
}
}
In this configuration dict the only required attribute is filter, it is a list of str names for serializer fields that we will use to find old model data, you can use up to 2 filters inside the filter attr the first every one of them has some special logics...
let filter=['id', 'name']
- in the first filter
id
we will search for an item withid=filter['id']
if we found one we use it else we raise an exception. - but in the second filter
name
we will search for an item withname=filter['name']
if we found one we use it else we create a new item withname=filter['name']
. - if there was a third filter then we wil skip it in this library version.
Here the filter attribute is the only required attribute the rest of them can be removed, and the library will set its default values.
views:
Last step is defining out ViewSets...
class C_ViewSet(NestedModelViewSet):
queryset = C.objects.all()
serializer_class = C_Serializer
class B_ViewSet(NestedModelViewSet):
queryset = B.objects.all()
serializer_class = B_Serializer
class A_ViewSet(NestedModelViewSet):
queryset = A.objects.all()
serializer_class = A_Serializer
As you can see our ViewSets are so brief and simple thanks to the abbreviation of all the operation of the nested models.
Using The Api
Now we can run the project and try our new api...
POST:
{
"charfield": "a",
"b": [
{
"charfield": "b",
"c": {
"charfield": "c"
}
}
]
}
This will create model A first and then will start creating model B data and inserting it to field A, and the same thing with model C data.
POST: with using old data...
{
"charfield": "a",
"b": [
{
"id": 1
}
]
}
or
{
"charfield": "a",
"b": [1]
}
Here we use an old B model data with id=1.
PUT & PATCH:
{
"id": 1,
"charfield": "a_updated_name",
"b": [
{
"charfield": "b",
"c": {
"charfield": "c"
}
}
]
}
This will get model A data with id=1 and update it's a var to a_updated_name
and create new model B data and set it to out A model that we get first.
PUT & PATCH: with old data...
{
"id": 1,
"charfield": "a_updated_name",
"b": [2]
}
Here it will get model A data with id=1 and add new b var data with id=2.
In short, you can...
- you can create nested models that are inside other models.
- you can update models by setting filter attr value (e.g.
id=1
, orusername="nameer"
). - you can set old models data by using just the filter attr value without specifying attr name (e.g.
m2m_relation_field=[1,2,3]
orforeignkey_field="nameer"
)
and you can't:
- you can't update old models in create operation.
and that's it for today 😁
MIT License
Copyright (c) 2022 Nameer Haider
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for DynamicNestedField-0.0.6-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c1b2f70db29e0989a62a59707a3a19e44d8d96c1a736e5ed3af2fca0f494ccbf |
|
MD5 | f26a50127724c009acbc15243e40d6e0 |
|
BLAKE2b-256 | 3e6b30d021c6d0e80919f36e0d3d67e43fa5b5fadc52cf0eb8a2ba58841b7667 |