Django Rest - get related data in M2M relation - django-rest-framework

I have 2 models:
class Artist(models.Model):
name = models.CharField(max_length=255)
music_type = models.CharField(max_length=50)
class Event(models.Model):
event_name = models.CharField(max_length=255)
description = models.TextField()
place = models.CharField(max_length=50)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
latitude = models.DecimalField(max_digits=9, decimal_places=6)
date = models.DateField()
artists = models.ManyToManyField(Artist)
For every artist I would like to get list of other artists if they have ever been in any event together.
I was able to create close solution, but only for specific artist:
def get_related_artists(request):
artist_id = 2
related_events = Artist.objects.filter(id=artist_id).first().event_set.all()
related_artists_ids = []
for event in related_events:
related_artists_ids = related_artists_ids + list(event.artists
.all()
.values_list('id', flat=True)
.all())
related_artists = Artist.objects\
.filter(id__in=set(related_artists_ids))\
.exclude(id=artist_id)
serializer = ArtistRelatedSerializer(related_artists, many=True)
return JsonResponse(serializer.data, safe=False)
So firstly I get all event where specific artist took part. Later I iterate over this events and get other artist's ids. Another step is to remove duplicated ids and specific artist id. At the end I use serializer to return data.
Serializer looks like:
class ArtistRelatedSerializer(serializers.ModelSerializer):
class Meta:
model = Artist
fields = '__all__'
Unfortunately I think it isn't optimal solution and works only for hardcoded artist's id. I would like to get all artists and for ech list of other artists.
I was thinking about creating loop and iterate over Artist.objects.count() but I couldn't find a solid solution to maintain all this queries.
Is there any other, maybe easier way to solve this solution?

You can solve this issue from Serializer by using SerializerMethodField. Get all events in each Artist, the example below:
class ArtistSerializer(serializers.ModelSerializer):
class Meta:
model = Artist
fields = ('id', 'name', 'music_type', 'events')
events = serializers.SerializerMethodField()
def get_events(self, obj):
events_qs = Event.objects.filter(artists__in=[obj.id])
events = EventSerializer(
events_qs, many=True, context=self.context).data
return events
To avoid the error by the circle imports, you should use import in the function

Related

Django REST: Exclude some fields and filter a nested serializer

I am trying to exclude the field "prospect" from the Appointment serializer, which is nested under the Unit Serializer.
As you can see below, due to some data manipulation on the Appointment queryset, I decided to use a SerializerMethodField to define the Appointment serializer. However, I am not sure how to exclude fields when using a SerializerMethodField.
Alternatively, I could use a ModelSerializer for the Appointment, which allows me to define which fields to include, but then I would not be able to manipulate the data the way I want.
Note sure what to do.
Models
class Appointment(models.Model):
appointment_time = models.DateTimeField()
unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
prospect = models.ForeignKey(Prospect, on_delete=models.CASCADE)
Serializers
class UnitSerializer(serializers.ModelSerializer):
availability = SerializerMethodField()
manager = ManagerSerializer()
instruction = InstructionSerializer(source='instruction_set', many=True)
appointment = SerializerMethodField()
class Meta:
model = Unit
fields = ['id', 'address', 'manager', 'availability', 'instruction', 'appointment']
def get_availability(self, instance):
queryset = instance.availability_set.order_by('start_time')
return AvailabilitySerializer(queryset, many=True).data
def get_appointment(self, instance):
start_buffer = 0.5 ## How many hours from now do we start displaying appointments in the queue
end_buffer = 72 ## How many hours from now do we stop displaying appointments in the queue
start_cutoff = datetime.now() + timedelta(hours=start_buffer)
end_cutoff = datetime.now() + timedelta(hours=end_buffer)
queryset = instance.appointment_set.exclude(appointment_time__lt=start_cutoff).exclude(appointment_time__gt=end_cutoff).order_by('appointment_time')
return AppointmentSerializer(queryset, many=True).data
JSON Object Representation

How to post data to Embedded document with Mongoengine REST

I am trying to use Django with mongoengine to make an API.
So far I can get the objects and delete them. but when I want to post some data. Lets say student + course it is giving an error:
type object 'Course' has no attribute 'objects'
Models en ..
#Model.py
class Course(EmbeddedDocument):
course_name = StringField(max_length=200)
course_fee = StringField(max_length=200)
class Student(Document):
student_name = StringField(max_length=200)
student_contactperson = StringField(max_length=200)
student_adress = StringField(max_length=200)
courses = ListField(EmbeddedDocumentField(Course))
#Serializers.py
class CourseSerializer(EmbeddedDocumentSerializer):
class Meta:
model = Course
fields = ('course_name','course_fee')
class StudentSerializer(DocumentSerializer):
courses = CourseSerializer(many=True)
class Meta:
model = Student
fields = ('student_name','student_contactperson','student_adress','courses')
depth = 2
def create(self, validated_data):
course_data = validated_data.pop('courses')
student = Student.objects.create(**validated_data)
Course.objects.create(student=student, **course_data)
return student
#Views.py
class StudentViewSet(meviewsets.ModelViewSet):
lookup_field = 'name'
queryset = Student.objects.all().order_by('-date_joined')
serializer_class = StudentSerializer
A Document represents a MongoDB document (i.e a record in a collection), a Document class is bound to a particular collection. An EmbeddedDocument represents a structure that gets nested in a Document.
So by design an EmbeddedDocument isn't attached to any collection unless you embed it inside a Document.
This means that you can't query or save an EmbeddedDocument class, you need to query/save the parent Document.
Document.objects is an entry point for querying a collection, it only exists on Document classes. You are calling Course.objects.create but Course is an EmbeddedDocument.
I believe you need to change your code to the following
class StudentSerializer(DocumentSerializer):
...
def create(self, validated_data):
course_data = validated_data.pop('courses')
course = Course(**course_data) # assuming course_data is {course_name: ..., course_fee: ...}
return Student.objects.create(courses=[course], **validated_data)

Manipulate data on serializer before creating model instance in DangoRestFramework

I have three related models as such
Order model
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
Name = models.CharField(max_length=250)
orderType = models.ForeignKey(OrderType, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ['id']
def __str__(self):
return '{}'.format(self.Name)enter code here
OrderPricing Model
class OrderPricing(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
TotalPrice = models.DecimalField(decimal_places=2, max_digits=10)
#related field
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ['order']
def __str__(self):
return self.TotalPrice
OrderType Model
class OrderType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
Name = models.CharField(max_length = 100)
Premium = models.BooleanField()
class Meta:
ordering = ['id']
Let's ignore the order in which the models appear above.
I have three SerializerModels for each model.
I can crud each model on the BrowsableAPI
Q1:
From the browsableAPI I can create an Order.
I haven't gotten to the 'Writable Nested Serializer' yet and I believe Django has that figured out in their docs through the drf-writable-nested class.
I have two orderTypes
1 = {'Not Premium':'False'} #not Premium
2 = {'Premium':'True'} #Premium
Assume I have a variable order_price = 5 #£5
How can I
Create an order,
If order is premium, then set order_price to 10 #order_price * 2
If order is NOT premium, then set order_price to 5
Create an instance of OrderPricing, that's related to the order. Also, pass the order_price variable to the property TotalPrice when creating the instance
from what I have seen and tried, I can override the Create() on the serializer as such
class OrderSerializer(WritableNestedModelSerializer):
"""OrderSerializer"""
# orderPricing = OrderPricingSerializer(required=False)
class Meta:
model = Order
fields = ('__all__')
def create(self, validated_data):
#create instance of order
#determine of order is premium
typeid = uuid.UUID(validated_data.pop('orderType'))#get FK value
isPremium = OrderType.objects.get(id = str(typeid.id))#determine if **Premium** is True/False
# set/calculate the price of the order
#create a related instance of OrderPricing
Q2
I am aware of GenericViews and the CreateModelMixin, what I don't know is, which is better, overriding the .create() at the serializer or overriding the CreateModelMixin method at the GenericView
Well, where to put business logic is always question hard to answer.
You have multiple places where it can be - view, serializer, model or some other separate module/service.
All have pros and cons- you can find many articles on this topic.
But in your case, I would probably go with perform_create of your view and I would create a method in the serializer which would update the price. If I needed to use the code to update price, I'd move to separate shared module and call it from there.
So let's say you use CreateModelMixin or better ListCreateAPIView
class YourView(ListCreateAPIView):
serializer = OrderSerializer
queryset = your_queryset
def perform_create(self, serializer):
serializer.update_price()
serializer.save()
perform_create is called after data is validated, so you can access the validated data.
update_price is your code where you update the price.
You can argue to move this logic to serializer's create or save method but they do many other things, so unless you need to override these methods for other reasons - you can take advantage of the perform_create method.

Confused to Serializer relations in Django Rest

In the Serializer relations part on Django official website, it provides a music albums example to represent and explain some relationship.
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks')
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
class Meta:
unique_together = ('album', 'order')
ordering = ['order']
def __unicode__(self):
return '%d: %s' % (self.order, self.title)
Later, it interacts model with Serializer.
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
The result is:
{
'album_name': 'Things We Lost In The Fire',
'artist': 'Low',
'tracks': [
'1: Sunflower',
'2: Whitetail',
'3: Dinosaur Act',
...
]
}
I am confused variable tracks in the AlbumSerializer. It seems that the variable tracks is irrelevant to Album and Track model.How it is related to the Album and Track models? How it calls and invokes unicode method of Track model in the AlbumSerializer?
It is related via related_name
album = models.ForeignKey(Album, related_name='tracks')
The related_name attribute specifies the name of the reverse relation from Album to Track.
Read more https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.ForeignKey.related_name

Django Rest Framework - return reverse foreign key property?

I think this is simple and probably a duplicate, but I cannot figure it out by looking at the documentation.
I have Django models as follows:
class Image(models.Model):
manor = models.ForeignKey(Manor, related_name='image_for_manor')
filename = models.CharField(max_length=30, null=True, blank=True)
class Manor(models.Model):
id = models.IntegerField(primary_key=True)
I want the user to be able to query the Manor and see the related Image. I'd like this JSON to be returned:
{
id: 572,
image: 'my/filepath.png'
}
This is my view:
#api_view(['GET'])
def manor(request, id):
mymanor = Manor.objects.get(id=id)
serializer = ManorSerializer(mymanor)
return JSONResponse(serializer.data)
And these are my serializers:
class ImageFilePathSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = ('filename',)
class ManorSerializer(serializers.ModelSerializer):
image = ImageFilePathSerializer(source="image_for_manor")
class Meta:
model = Manor
fields = ('id', 'image')
But this doesn't work: I get an empty dictionary for image. (Even if it weren't empty, I realise it wouldn't be right, because I don't want the image property to be a dictionary: I want it to be a string.)
How can I change this to be correct? I cannot work it out.
As Kevin suggested , why dont you use ImageField which will give you url where your image is uploaded. although if you dont want to do that , here are some changes you have to do to get the result format you wanted.(Following solution assumes that only one image will be for one minor)
views.py ( why dont you ImageFilePathSerializer instead of another one as it also contains all the data you wanted)
#api_view(['GET'])
def manor(request, id):
mymanor = Image.objects.get(manor__id=id)
serializer = ImageFilePathSerializer(mymanor)
return Response(serializer.data)
serializers.py (add Id with filename in ImageFilePathSerializer)
class ImageFilePathSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = ('id', 'filename',)
class ManorSerializer(serializers.ModelSerializer):
filename = serializers.ImageField(source="image_for_manor")
class Meta:
model = Manor
fields = ('id', 'filename')

Resources