I have a Django REST framework application with a course and department model. I have the following models, serializers and viewset to represent the course catalog.
## MODELS ##
class Department(models.Model):
"""Academic Department"""
name = models.CharField(max_length=30, unique=True)
class Course(models.Model):
"""Courses in the course catalog"""
course_name = models.CharField(max_length=30, unique=True)
department = models.ForeignKey(Department, on_delete=models.PROTECT)
course_number = models.DecimalField(decimal_places=0, max_digits=3, unique=True)
credits = models.IntegerField()
class Meta:
unique_together = (("department", "course_number"),)
## SERIALIZERS ##
class DepartmentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Department
#fields = ('id', 'last_name', 'first_name')
class CourseSerializer(serializers.ModelSerializer):
department = serializers.SlugRelatedField(
many = False,
read_only = False,
slug_field = 'name',
queryset = models.Department.objects.all()
)
class Meta:
model = models.Course
fields = (
'id',
'course_name',
'department',
'course_number',
'credits')
## VIEWSETS ##
class CourseViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows courses to be viewed or edited.
"""
queryset = models.Course.objects.all().order_by('department__name','course_number')
serializer_class = serializers.CourseSerializer
Course has a foreign key pointing to department and using the SlugRelatedField a courses department is listed in the result JSON as the department name:
{
"count": 5,
"next": null,
"previous": null,
"results": [
{
"id": 5,
"course_name": "History of Justin",
"department": "English",
"course_number": "344",
"credits": 3
},
{
"id": 6,
"course_name": "34",
"department": "Math",
"course_number": "4",
"credits": 4
},
{
"id": 1,
"course_name": "Pre Calc",
"department": "Math",
"course_number": "101",
"credits": 3
},
{
"id": 2,
"course_name": "Calc I",
"department": "Math",
"course_number": "200",
"credits": 3
},
{
"id": 3,
"course_name": "Calc II",
"department": "Math",
"course_number": "201",
"credits": 3
}
]
}
I can also populate new courses with the following body in a POST request:
{
"course_name": "Chemistry I",
"department": "Science",
"course_number": "200",
"credits": 3
}
However, the auto generated form does not render the slug field text.
.
How can I change what this form renders?
You'll need to add a __str__ method to Department in order to tell Django about how to represent a Department instance:
class Department(models.Model):
"""Academic Department"""
name = models.CharField(max_length=30, unique=True)
def __str__(self):
return self.name
Related
I want to customise the output of serializer to group some fields of the models into a nested dictionary, so I override to_representation:
My models.py
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
class Meta:
ordering = ['created']
My serializers.py
class SnippetSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Snippet
fields = ['id', 'title', 'code', 'linenos', 'language', 'style', 'owner']
def to_representation(self, instance):
return {
'id': instance.id,
'owner': instance.owner.username,
'therest': {
'title': instance.title,
'code': instance.code,
'linenos': instance.linenos,
'language': instance.language,
'style': instance.style
}
}
class UserSerializer(serializers.ModelSerializer):
snippets = SnippetSerializer
class Meta:
model = User
fields = ['id', 'username', 'snippets']
depth = 1
My expected outcome of users serializer is:
[
{
"id": 1,
"username": "admin",
"snippets": [
{
"id": 1,
"owner": "admin",
"therest": {
"title": "",
"code": "foo=\"bar\"\\n",
"linenos": false,
"language": "python",
"style": "abap"
}
}
]
}
]
but I got this instead:
[
{
"id": 1,
"username": "admin",
"snippets": [
{
"id": 1,
"created": "2022-09-01T17:11:59.743112Z",
"title": "",
"code": "foo=\"bar\"\\n",
"linenos": false,
"language": "python",
"style": "abap",
"owner": 1
},
]
}
]
Nested snippets in UserSerializer does not inherit to_representation from SnippetSerializer. Did I miss something?
I have Parent-Children Relation Table in Model which is as follows:
models.py
class ProductCategory(models.Model):
parent = models.ForeignKey(to='ProductCategory', blank=True, null=True, related_name="sub_cats", on_delete=models.CASCADE)
name = models.CharField(max_length=30, blank=False, null=False)
desc = models.TextField(blank=True, null=True, db_column='description')
def __str__(self):
return self.name
Data in table
Using this data I want to make a tree in react component where a user see how many types of categories a product has.
right now getting this
[
{
"id": 1,
"parent": null,
"name": "Electronics",
"desc": "All kinds of electronics items comes in this category",
"sub_cats": [
{
"id": 2,
"parent": 1,
"name": "Mobiles",
"desc": "Category for Smartphones, Features-Phone, etc.",
"sub_cats": [3]
}
]
},
{
"id": 2,
"parent": 1,
"name": "Mobiles",
"desc": "Category for Smartphones, Features-Phone, etc.",
"sub_cats": [
{
"id": 3,
"parent": 2,
"name": "X-Mobile",
"desc": "All Smartphones of X-Mobile",
"sub_cats": []
}
]
},
{
"id": 3,
"parent": 2,
"name": "X-Mobile",
"desc": "All Smartphones of X-Mobile",
"sub_cats": []
}
]
or any other idea for making tree
We have the SecurityEventItem that have the owner taken from another security_location model and returned.
The logic behind not having security_location direct as a foreign key is that we get events with security_location names that are not added yet to database, but we want them to be registered without adding the missing security location to the database.
class SecurityEventItemGetSerializer(serializers.ModelSerializer):
epc = RfidTagNestedSerializer()
owner = serializers.SerializerMethodField('get_owner')
def get_owner(self, member):
location = SecurityLocationSerializer(models.SecurityLocation.objects.get(name=member.security_location)).data
owner_name = ProductOwnerSerializer(models.ProductOwner.objects.get(id=location["owner"])).data
return owner_name["owner_name"]
class Meta:
model = models.SecurityEventItem
fields = ["id", "epc", "acknowledged", "firstSeenTimestamp", "security_location", "owner"]
The ViewSet bellow
class SecurityEventItemGetViewset(viewsets.ModelViewSet):
"""SecurityEventItemGet Viewset
API endpoint that allows security event items to be viewed.
Allowed actions:
"GET"
"""
queryset = models.SecurityEventItem.objects.all()
serializer_class = serializers.SecurityEventItemGetSerializer
http_method_names = ['get']
filter_backends = [DjangoFilterBackend]
search_fields = ["owner"]
filterset_fields = {
'acknowledged': ['exact'],
'epc': ['exact', "in"],
'security_location': ['exact', "in"],
'firstSeenTimestamp': ['gte', 'lte'],
}
I have tryed SearchFilter unsuccesfull as simillar to DjangoFilterBackend it does not recognize the owner field added that does not belong to the SecurityEventItem model.
The response is like this: and I would like to make a filter on the request passing a parameter "owner" added to the existing ones.
[
{
"id": 73,
"epc": {
"id": 8371,
"number": "1234",
"product": {
"id": 1,
"name": "default_product",
"ean": "",
"description": "default product for foreign/unknown RFID tags",
"category": {
"id": 1,
"name": "default_category",
"description": "default category for foreign/unknown RFID tags"
},
"info": []
},
"state": 1,
"info": [],
"owner": {
"id": 1,
"owner_id": 1,
"owner_name": "George"
}
},
"acknowledged": false,
"firstSeenTimestamp": "2022-02-21T09:44:08",
"security_location": "Test Location",
"owner": "Second Owner"
},
{
"id": 72,
"epc": {
"id": 105177,
"number": "303625D4580B2484000002CA",
"product": {
"id": 590,
"name": "A78R07",
"ean": "5940000792305",
"description": "Fata de perna 50x70",
"category": {
"id": 1,
"name": "default_category",
"description": "default category for foreign/unknown RFID tags"
},
"info": "{\"company\": \"HOTEL Nr1\"}"
},
"state": 1,
"info": [],
"owner": {
"id": 1,
"owner_id": 1,
"owner_name": "Regina"
}
},
"acknowledged": false,
"firstSeenTimestamp": "2022-02-21T09:31:16",
"security_location": "Front Desk",
"owner": "Second Company"
}
]
I would really appreciate if someone could teach me how to do that, there are plenty of information regarding model filtering, but no filters for the filters added to extra fields
SerializerMethodField is read-only.
This field is generated only at the time of serialization and does not exist in the database.
Filters work only with the database, but the owner field is not there.
I think you should annotate the queryset with the data you want (.annotate(owner=...), Subquery() ), then the filter can work because that field will be returned from the database.
Like nested dict of list ?Can we use serializers to return customise or we need to write django view to customize the output of data ?
models.py
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
view.py
class ReporterView(generics.ListCreateAPIView):
queryset = Reporter.objects.all()
serializer_class = ReportSerializer
class ArticleView(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
urls.py
urlpatterns = [
path('reporter/',ReporterView.as_view(),name='reporter'),
path('artical/',ArticleView.as_view(),name='article'),
]
These apis return data as follow :
for api reporter/:
[
{
"id": 1,
"first_name": "Nero",
"last_name": "Sutton",
"email": "dugunox#mailinator.com"
},
{
"id": 2,
"first_name": "Maia",
"last_name": "Stark",
"email": "kacepub#mailinator.com"
}
]
for api 'artical/'
{
"id": 1,
"headline": "Consequatur",
"pub_date": "2020-02-14",
"reporter": {
"id": 1,
"first_name": "Nero",
"last_name": "Sutton",
"email": "dugunox#mailinator.com"
}
},
Now i want to create new api which can return data in following format:
[
{ "first_name": "Nero",
"email": "dugunox#mailinator.com"
"reportinf_artical":[
{ "headline": "Consequatur1", "pub_date": "2020-02-14",},
{ "headline": "Consequatur2", "pub_date": "2020-02-14",},
{ "headline": "Consequatur3", "pub_date": "2020-02-14",},
]
},
{ "first_name": "Nero",
"email": "dugunox#mailinator.com"
"reportinf_artical":[
{ "headline": "Consequatur1", "pub_date": "2020-02-14",},
{ "headline": "Consequatur2", "pub_date": "2020-02-14",},
{ "headline": "Consequatur3", "pub_date": "2020-02-14",},
]
},
#and so on....
]
There is one model field called ListField from the django_mysql.models module that may be applicable for this.
from django.db.models import Model, CharField
from django_mysql.models import ListCharField
class MyPurchase(Model):
category = CharField(max_length=20)
purchase_date = DateField(auto_now_add=True)
products = ListCharField(
base_field = CharField(max_length=100)
)
It may look something of this sort. You can set the number of items you can add to products. Have a look at its docs if you want to read further into it.
https://django-mysql.readthedocs.io/en/latest/model_fields/list_fields.html
Hope this helps.
I have a one-to-many relationship model with payments and paymentlines. How can I group all the paymentlines with the same item and display in serializer. The key is the payments belong to the same order which order id is one the attributes in payments model.
views.py
class OrderItemViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Payment.objects.all()
def get_queryset(self):
return self.queryset.filter(order_id=self.request.GET.get("order_id"))
serializer_class = PaymentSerializer
serializers.py:
class PaymentLineSerializer(ModelSerializer):
class Meta:
model = PaymentLine
fields = (
"item_number",
"currency",
"available_quantity",
"order_total_amount",
)
class PaymentSerializer(ModelSerializer):
payment_lines = PaymentLineSerializer(PaymentLine.objects.select_related("payment"), many=True)
class Meta:
model = Payment
fields = ("order_id", "total_amount", "currency", "payment_lines")
To display this json:
[
{
"order_id": 4,
"total_amount": "250.964024",
"currency": "USD",
"payment_lines": [
{
"item_number": 1100,
"currency": "USD",
"available_quantity": 0,
"order_total_amount": 0,
},
{
"item_number": 1200,
"currency": "USD",
"available_quantity": 1,
"order_total_amount": 224.982012,
}
]
}
]
instead of this:
[
{
"order_id": 4,
"total_amount": "444.700000",
"currency": "USD",
"payment_lines": [
{
"item_number": 1100,
"currency": "USD",
"available_quantity": 1,
"order_total_amount": 25.982012,
},
{
"item_number": 1200,
"currency": "USD",
"available_quantity": 1,
"order_total_amount": 224.982012,
}
]
},
{
"order_id": 4,
"total_amount": "-25.982012",
"external_reference_type": "MAG",
"currency": "USD",
"payment_lines": [
{
"item_number": 1100,
"currency": "USD",
"available_quantity": -1,
"order_total_amount": -25.982012,
}
]
}
]
How about this. I assums that paymentline_set is your related name in models.py
from django.db.models import Prefetch
class OrderItemViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
return Payment.objects.filter(
order_id=self.request.GET.get("order_id")
).prefetch_related(
Prefetch(
'paymentline_set',
queryset=PaymentLine.objects.all(),
to_attr='payment_lines'
)
)
class PaymentSerializer(ModelSerializer):
payment_lines = PaymentLineSerializer(many=True)
class Meta:
model = Payment
fields = ("order_id", "total_amount", "currency", "payment_lines")
You can use without Prefetch(), but .prefetch_related('paymentline_set') then in PaymentSerializer change payment_lines to paymentline_set.