I am new to Django have background in Node.js.
I am creating an API using Django, Django rest framework and PostgreSQL.
# Model for Event.
class Event(models.Model):
heading = models.TextField()
event_posted_by = models.ForeignKey(
CustomUser, on_delete=models.CASCADE, related_name='events_posted', null=True)
event_construction_site = models.ForeignKey(
ConstructionSite, on_delete=models.CASCADE, related_name='construction_site_events', null=True)
def __str__(self):
return str(self.id)
# Event ViewSet
class EventViewSet(viewsets.ModelViewSet):
queryset = Event.objects.all()
serializer_class = EventSerializer
# Event Serializer
class EventSerializer(serializers.ModelSerializer):
event_construction_site = ConstructionSiteShortSerializer()
event_posted_by = CustomUserSerializer()
class Meta:
model = Event
fields = ('id', 'heading',
'event_posted_by', 'event_construction_site')
Everything here works fine and expected as well.
Here is the output of my GET Request.
{
"id": 2,
"heading": "ABCD",
"event_posted_by": {
"id": 1,
"email": "abcd#gmail.com",
"first_name": "A",
"last_name": "B",
"profile_picture": "...",
"company": {
"id": 3,
"name": "Company 3"
},
},
"event_construction_site": {
"id": 2
}
},
But now when it comes to create an event this is the view django rest framework shows me.
{
"heading": "",
"event_posted_by": {
"email": "",
"first_name": "",
"last_name": "",
"company": {
"name": ""
},
"profile_picture": "",
"user_construction_site": []
},
"event_construction_site": {}
}
Here as far as I know when need "event_posted_by" by in GET Request but we don't want to post the user info. while creating an event, same for information inside user like user_construction_site here the only behaviour I want is to send the User ID to backend and somehow in my class EventViewSet(viewsets.ModelViewSet) handle this post request and map "event_posted_by" to user data by using user ID sent from client side.
How these kind of problems are genrally handeled in DRF by creating different read / write serailizers?
I managed to resolve the problem by following suggestion by #BriseBalloches in the comment above.
In summary updated the event_posted_by by adding a read_only key.
event_posted_by = UserSerializer(read_only=True)
Related
I have a django model called “Enrolment” that depends on two other models through foreign key. “Course” model and “Enrolment” model. The “Course” model is like so:
class Course(models.Model):
name = models.CharField(max_length=100, blank=True)
description = models.TextField(blank=True)
prerequisite = models.TextField(blank=True)
fee = models.FloatField(default=0.0)
def __str__(self):
return self.name
And the Enrolment model is like so:
class CourseEnrolment(models.Model):
course_name = models.ForeignKey(
Course, on_delete=DO_NOTHING)
schedule = models.CharField(max_length=50, blank=True)
student = models.ForeignKey(
UserAccount, on_delete=DO_NOTHING)
enrolment_date = models.DateTimeField(default=datetime.now())
def __str__(self):
return self.course_name.name
For me to gain access to the information about the enrolled course, I have to do like so in the CourseEnrolmentSerializer:
class CourseEnrolmentSerializer(serializers.ModelSerializer):
class Meta:
model = CourseEnrolment
depth = 1
fields = '__all__'
With “depth = 1” being the secret to accessing information about the course. Now when I fetch enrolment, I get a response like below, which is exactly what I want like so.
{
"id": 5
"schedule": "June 23, 2022",
"course_name": {
"id": 4,
"name": "fundamentals-of-programming",
"description": "some description .",
"prerequisite": "some prerequisite",
"fee": 1000,
},
"student": {
"id": 1,
"email": "martinsakinbo#yahoo.com",
"first_name": "John",
"last_name": "Doe",
"phone": "1111111",
}
},
Now, the problem I have is that with “depth=1” in the serializer, I’m not able to create an enrolment when I write the following view
class CourseEnrolment(generics.CreateAPIView):
queryset = CourseEnrolment.objects.all()
serializer_class = CourseEnrolmentSerializer
Trying to create enrolment in postman by doing like so:
{
"course_name": 3,
"schedule": "June 23, 2022",
"student": 1
}
Generates the error the “null value in column "course_name_id" of relation "course_courseenrolment" violates not-null constraint”
I equally tried by nesting into the course_name id and student id by doing like so:
{
"course_name.id": 3,
"schedule": "June 23, 2022",
"student.id": 1
}
I get the same error.
But if I remove depth=1 from the serializer, everything works, and I’m about to create an enrolment
PLEASE HOW DO I CREATE (POST) AN ENROLMENT STILL WITH “depth=1”? OR IS THERE A BETTER OR DIFFERENT WAY I SHOULD WRITE MY VIEW. IF YES, HOW?
Make a new serializer specific for creating course enrollment:
class CreateCourseEnrollmentSerializer(serializers.ModelSerializer):
class Meta:
model = CourseEnrolment
fields = "__all__"
And inside the view define get_serializer_class() method in the following way (which you may want to use if your view supports more the one HTTP action. Actions are available to rest_framework.viewsets):
from rest_framework import viewsets
from .serializers import CreateCourseEnrollmentSerializer, CourseEnrolmentSerializer
class CourseEnrollmentView(viewsets.ModelViewSet):
# ...
def get_serializer_class(self):
if self.action == "create":
return CreateCourseEnrollmentSerializer
return CourseEnrolmentSerializer
And in the app's urls.py:
from .views import CourseEnrollmentView
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"course-enrollments", CourseEnrollmentView)
urlpatterns = [
# ...
]
urlpatterns += router.urls
I want to add Dynamic Field Array serializer in my drf project:
My get response looks something like this:
{
"title": "some",
"created_at": "2022-03-06T15:59:52.684469Z",
"fields": [
{
"id": 1,
"title": "Some title?",
"parent_field": 1
},
{
"id": 2,
"title": "Yet another fields",
"parent_field": 1
}
]
}
This is the item detail serializer, and fields is another model serializer. I achieved this by using this code:
class AnotherFieldSerializer(serializers.ModelSerializer):
class Meta:
model = AnotherModel
fields = "__all__"
class FirstSerializer(serializers.ModelSerializer):
fields = serializers.SerializerMethodField()
class Meta:
model = FirstModel
fields = "__all__"
def get_fields(self, obj):
serializer_context = {'request': self.context.get('request')}
children = obj.fields
if not children.exists():
return None
serializered_children = FirstSerializer(
children,
many=True,
context=serializer_context
)
return serializered_children.data
This works only for GET requests I want this to also work with POST and PUT requests. So, imagine I want to add/edit an item into my model FIRST model and add fields associated with it by just sending this JSON:
{
"title": "some",
"created_at": "2022-03-06T15:59:52.684469Z",
"fields": [
{
"id": 1,
"title": "Some title?",
},
{
"id": 2,
"title": "Yet another fields",
}
]
}
I know I can get fields from the response and loop through each item to create an instance of Another Model but fields validation will be much harder I think. But if there's more of a drf of doing this thing then it would be great. Also, I have no problem with making another serializer just for POST and PUT requests.
I hope my English was understandable.
You can create a new serializer for validation. To validate multiple items you can do the following,
class PostSerializer(serializers.Serializer):
title = serializers.CharField(max_length=200)
created_at = serializers.DateTimeField()
fields = FirstSerializer(many=True) # your model serializer for the FirstModel
Ref : https://www.django-rest-framework.org/api-guide/serializers/#listserializer
Background:
I use mixed webframeworks: openresty and django over same database. I use django mainly for database schema update and admin site. The best result would be that Django can define a model by some json.
Does Django has anything to handle this? for example, convert:
class Log(models.Model):
ctime = models.DateTimeField(blank=True, null=True)
message = models.CharField(max_length=1024)
class Meta:
managed = True
db_table = 'log'
to its json form, something like:
{
"db_table": "log",
"managed": true,
"fields": [
{"name": "ctime", "type": "datetime","null":true},
{"name": "message", "type": "char", "maxlength":1024},
],
}
Or define a model by json:
class Log(models.Model):
class Meta:
json = {...}
hi guys i want to serialize my model's object. and my model like this :
class User (models.Model):
name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
and i have another model like this:
class Child (models.Model):
little_name = models.CharField(max_length=30)
user = models.ForeignKey("User", on_delete=models.CASCADE,)
i want to serialize one of my child object with all fields in user field for example like this:
{"id": 1 ,
"little_name": "Sam",
"user": {"id": 1,
"name": "Karim",
"family_name":"Kari"
}
}
i use model_to_dict() and dict() and serializer of django but i can't get user field compeletly and they return it like this :
{"id": 1 ,
"little_name": "Sam",
"user": "id": 1,
}
and i don't want to use django rest serializer
what can i do ?!?
Use model_to_dict twice:
child_dict = model_to_dict(child)
child_dict['user'] = model_to_dict(child.user)
We have a Client model, and a ClientTeamMember model which allows one or more team members to be assigned to each client. TeamMember in our site is a profile extension of the User account.
In the ClientTeamMember table, we also have a groups field, which is the same Group model defined in /django/contrib/auth. Its purpose is to be able to assign a group (role) to a TeamMember for a specific client, giving them extra permissions that they don't have for other clients. Like, for example, making a team member the "Project Manager" for only one client.
Our models:
class Client(models.Model):
name = models.CharField(max_length=100)
class ClientTeamMember(models.Model):
client = models.ForeignKey(
Client,
related_name='client_team_members',
)
team_member = models.ForeignKey(
TeamMember,
related_name='team_member_clients',
)
groups = models.ManyToManyField(
Group,
blank=True,
default='',
related_name='client_groups',
)
We want to return the group names (not just the ids) in the Client serializer. Like this:
"results": [
{
"id": 9,
"name": "Acme Honey",
"permission_groups": [
"Project Manager"
]
}
]
Just as on the User model, Group is a many-to-many field, allowing for multiple groups to be assigned at any level.
Here are the serializers:
class GroupNameSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ['name']
class ClientGroupsSerializer(serializers.ModelSerializer):
groups = GroupNameSerializer(many=True, read_only=True)
class Meta:
model = ClientTeamMember
fields = 'groups'
class ClientSerializer(serializers.ModelSerializer):
permission_groups = ClientGroupsSerializer(source='client_team_members', many=True)
class Meta:
model = Client
fields = '__all__'
With the above configuration, this is what it returns:
"results": [
{
"id": 9,
"name": "Acme Honey",
"permission_groups": [
{
"groups": [
{}
]
}
]
}
]
If I remove the groups = GroupNameSerializer(...) line, and change fields to __all__, I get this:
"results": [
{
"id": 9,
"name": "Acme Honey",
"permission_groups": [
{
"id": 1,
"client": 9,
"team_member": 3
}
]
}
]
...so I know it's getting the ClientTeamMember row (which is where the "Project Manager" group is). I don't understand why it doesn't include the groups field in that case, though.
Things I've tried:
Added level = 1, 2, etc.
Changed the name of the groups field in ClientGroupSerializer and added source="groups" to it.
Changed groups to a ReadOnlyField (and changed its name).
Overrode the to_representation method in ClientGroupSerializer
And the ultimate goal is to return a list of names, instead of "name": "Project Manager" as shown in my first "results" example above.
you can do this way:
class ClientSerializer(serializers.ModelSerializer):
permission_groups = serializers.SerializerMethodField('get_group_names')
class Meta:
model = Client
fields = ('id','name','permission_groups')
def get_group_names(self,obj):
group_name_list = [group.name for group in obj.client_team_members] #assuming group has a name field
return group_name_list