I'm using DRF and have object with ManyToMany field. I'd like to check if object that user sent on server contains any pk in that field. Then i want to set boolean field to true in linked object to that ManyToMany.
Models:
class Parent(models.Model):
child_link = models.ManyToManyField(child, related_name="child")
class Child(models.Model):
in_use = models.BooleanField(default=False)
Views:
class ParentView(viewsets.ModelViewSet):
serializer_class = ParentSerializer
authentication_classes = (SessionAuthentication, )
def get_queryset(self):
user = self.request.user
return Parent.objects.filter(user=user)
class ChildView(viewsets.ModelViewSet):
serializer_class = ChildSerializer
authentication_classes = (SessionAuthentication, )
def get_queryset(self):
user = self.request.user
return Child.objects.filter(user=user)
Serializers:
class ParentSerializer(serializers.ModelSerializer):
class Meta:
model = Parent
fields = __all__
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = Child
fields = __all__
In this specific case i described, the best solution is to add create() to parent serializer:
class ParentSerializer(serializers.ModelSerializer):
def create(self, *args, **kwargs):
if len(self.data["child_link"]) != 0:
for id in self.data["child_link"]:
obj = Child.objects.get(pk=id)
obj.in_use = True
obj.save()
return super().create(*args, **kwargs)
class Meta:
model = Parent
fields = __all__
Related
Here is the issue.I am trying to delete the tags related to my model. I can create them but whenever i try to use HTTP DELETE on ExampleModelTags, i face with the following issue.
Error: Cannot resolve keyword 'author' into field. Choices are: id, examplemodel, examplemodel_id, tag, timestamp
I can't understand what is the issue, i can create them so the algorithm works. The tags can connect to their parent model . But whenever i try to do HTTP DELETE on ExampleModelTags, i get that issue. Where is the problem that i don't see?
Model.py
class ExampleModelTag(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
tag = models.CharField(max_length=35,null=True,blank=True)
examplemodel = models.ForeignKey(ExampleModel, on_delete=models.CASCADE,null=True,blank=True, related_name='examplemodeltags')
timestamp = models.DateTimeField(auto_now_add=True)
class ExampleModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE,null=True,related_name='examplemodels')
examplemodel = models.CharField(unique=False,max_length=100,null=True,blank=True)
timestamp = models.DateTimeField(unique=False,auto_now_add=True)
Serializer.py
class ExampleModelTagSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleModelTag
fields = ("id","examplemodel","tag","timestamp")
def validate(self, attrs):
attrs = super().validate(attrs)
if attrs['examplemodel'].author.id != self.context['request'].user.pk:
raise ValidationError('Unauthorized Request')
return attrs
class ExampleModelSerializer(serializers.ModelSerializer):
examplemodeltags_set = ExampleModelTagSerializer(source='examplemodeltags',required=False,many=True)
class Meta:
model = ExampleModel
fields = ("id","author","examplemodel","examplemodeltags_set","timestamp")
def validate(self, attrs):
attrs = super().validate(attrs)
if attrs['author'].id != self.context['request'].user.pk:
raise ValidationError('Unauthorized Request')
return attrs
Views.py
class ExampleModelViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset = ExampleModel.objects.all().order_by('-timestamp')
serializer_class = ExampleModelSerializer
filter_backends = [UserFilterBackend]
class ExampleModelTagViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset = ExampleModelTag.objects.all()
serializer_class = ExampleModelTagSerializer
filter_backends = [UserFilterBackend]
Filters.py
class UserFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(
author=request.user
)
You can not use the UserFilterBackend on the ExampleModelTagViewSet, since an ExampleModelTag has no author field. You thus should rewrite the ExampleModelTagViewSet to:
class ExampleModelTagViewSet(viewsets.ModelViewSet):
# ⋮
filter_backends = [] # ← UserFilterBackend not applicable
If you want to fetch ExampleModelTags for which an ExampleModel exists that links to that ExampleModelTag record, and has as author the logged in user, you can specify this with:
class ExampleModelAuthorBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(
examplemodel__author=request.user
)
and then use that one to filter the ExampleModelTags:
class ExampleModelTagViewSet(viewsets.ModelViewSet):
# ⋮
filter_backends = [ExampleModelAuthorBackend]
class UserFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(
examplemodel__author=request.user
)
I have a modelset view in which different customs functions are defined based on the requirement. I have to write another get function in which I want to use the same serializer class. But the field which I have defined in the serializer class in pkfield but for the get function, I want it as a stringfield rather than pk field. How to achieve that??
Also, I have defined depth=1, which is also not working.
class Class(TimeStampAbstractModel):
teacher = models.ForeignKey(
Teacher,
on_delete=models.CASCADE,
null=True,
related_name="online_class",
)
subject = models.ForeignKey(
Subject,
on_delete=models.SET_NULL,
null= True,
related_name= "online_class",
)
students_in_class = models.ManyToManyField(Student, related_name="online_class")
My view:
class ClassView(viewsets.ModelViewSet):
queryset = Class.objects.all()
serializer_class = ClassSerializer
serializer_action_classes = {
'add_remove_students': AddStudentstoClassSerializer,
'get_all_students_of_a_class': AddStudentstoClassSerializer,
}
def get_serializer_class(self):
"""
returns a serializer class based on the action
that has been defined.
"""
try:
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
return super(ClassView, self).get_serializer_class()
def add_remove_students(self, request, *args, **kwargs):
"""
serializer class used is AddStudentstoClassSerializer
"""
def get_all_students_of_a_class(self,request,pk=None):
"""
for this I function too, I want to use the same AddStudentstoClassSerializer class but
there is a problem. The field students_in_class is already defined as pkfield, whereas I
want to use it as a stringfields in the response of this function
""""
My serializer:
class AddStudentstoClassSerializer(serializers.ModelSerializer):
students_in_class = serializers.PrimaryKeyRelatedField(
many=True, queryset=Student.objects.all()
)
class Meta:
model = Class
fields = ["students_in_class"]
depth = 1
def update(self, instance, validated_data):
slug = self.context["slug"]
stu = validated_data.pop("students_in_class")
/................other codes....../
return instance
Here we can see the student_in_class is defined as pkfield which is ok when using the update api, but when I want to use the get api and call get_all_students_of_a_class I want the field to be stringfield or some other field. How to do that? Also depth= 1 is also not working.
Update:
Treid the following but still not working:
def to_representation(self, instance):
rep = super().to_representation(instance)
# rep["students_in_class"] = instance.students_in_class
rep['students_in_class'] = StudentSerializer(instance.students_in_class).data
return rep
class StudentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Student
fields = ['user', 'college_name', 'address']
what i got in the response is
{
"students_in_class": {}
}
it is empty dict. what should be done!
You can override you to_representation method like this.
class AddStudentstoClassSerializer(serializers.ModelSerializer):
students_in_class = serializers.PrimaryKeyRelatedField(
many=True, queryset=Student.objects.all()
)
class Meta:
model = Class
fields = ["students_in_class"]
def to_representation(self, instance):
data = {
"students_in_class": # Write your logic here
}
return data
def update(self, instance, validated_data):
slug = self.context["slug"]
stu = validated_data.pop("students_in_class")
/................other codes....../
return instance
I have a view like this:
class UserDetail(generics.RetrieveDestroyAPIView):
permission_classes = [IsAuthenticatedOrReadOnly]
queryset = User.object.all()
serializer_class = UserSerializer
def get_object(self, queryset=None, **kwargs):
item = self.kwargs.get('pk')
return generics.get_object_or_404(User, id=item)
serializer like this:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'city']
and urls like this:
path('<uuid:pk>', UserDetail.as_view(), name='user_detail')
Can I using just one view and one serializer fetch in one case all data (id, fist_name, last_name and city) and in other case just the city by json? Or maybe I have to create for it especially a new view and serializer like this:
class UserCity(generics.RetrieveDestroyAPIView):
permission_classes = [IsAuthenticatedOrReadOnly]
queryset = User.object.all()
serializer_class = UserJustCitySerializer
def get_object(self, queryset=None, **kwargs):
item = self.kwargs.get('pk')
return generics.get_object_or_404(User, id=item)
and
class UserJustCitySerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['city']
You can try SerializerMethodField. Which will return the field if the condition is true inside your get_your_conditional_field method.
class YourSerializer(serializers.ModelSerializer):
your_conditional_field_name = serializers.SerializerMethodField()
class Meta:
model = model_name
def get_your_conditional_field(self, obj):
# do your conditional logic here
# and return appropriate result
return obj
I have a serializer that is meant to act as a template for other ModelSerializers.
class CountryBasedModelSerializer(ModelSerializer):
def __init__(self, data, context):
assert 'country' in self.Meta.fields
class Meta:
model = Country
fields = ()
I want to use it with this, which is the actual serializer which will be called.
class CountryBasedProjectSerializer(CountryBasedModelSerializer):
class Meta:
model = Project
fields = ('id', 'country', 'name')
I want to use it with this inherited viewset:
class CountryBasedViewset(viewsets.ModelViewSet):
queryset = None
serializer_class = CountryBasedModelSerializer
def get_queryset(self):
return self.queryset.filter(country_pk=self.request.data["country"])
And this is the actual viewset that will be called:
class CountryProjectBasedViewset(CountryBasedViewset):
queryset = Project.objects.all()
Is there anything that I am clearly doing incorrectly?
Just define the serializer_class for CounteryProjectBasedViewSet as below
class CountryProjectBasedViewset(CountryBasedViewset):
queryset = Project.objects.all()
serializer_class = CountryBasedProjectSerializer
I am setting up a Django REST application where peopple can review restaurants. So far I have those models:
class RestaurantId(models.Model):
maps_id = models.CharField(max_length=140, unique=True)
adress = models.CharField(max_length=240)
name = models.CharField(max_length=140)
class RestaurantReview(models.Model):
review_author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class StarterPics(models.Model):
restaurant_review_id = models.OneToOneField(RestaurantReview,
on_delete=models.CASCADE)
pics_author = models.ForeignKey(User, on_delete=models.CASCADE)
restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
name_1 = models.CharField(max_length=40)
picture_1 = models.ImageField()
My serializers:
class RestaurantIdSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantId
field = fields = '__all__'
class RestaurantReviewSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantReview
field = fields = '__all__'
class StarterPicsSerializer(serializers.ModelSerializer):
class Meta:
model = StarterPics
fields = '__all__'
def validate_restaurant_review_id(self, value)
if value.review_author != self.request.user:
raise serializers.ValidationError("User has not reviewed the restaurant")
return value
My views:
class RestaurantIdViewset(viewsets.ModelViewSet):
queryset = models.RestaurantId.objects.all()
serializer_class = serializers.RestaurantIdSerializer
class RestaurantReviewViewset(viewsets.ModelViewSet):
queryset = models.RestaurantReview.objects.all()
serializer_class = serializers.RestaurantReviewSerializer
permission_classes = [IsAuthenticatedOrReadOnly,IsAuthorOrReadOnly]
def perform_create(self, serializer):
serializer.save(review_author=self.request.user)
class StarterPicsViewset(viewsets.ModelViewSet):
queryset = models.StarterPics.objects.all()
serializer_class = serializers.StarterPicsSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
I have set up permissions as well so only the review_author can update his reviews and pics_author can update his pictures.
My permissions:
class IsOwnReviewOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.pics_author == request.user
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.review_author == request.user
When running Django server I got a 'StarterPicsSerializer' object has no attribute 'request'
This validation is for user that have not written the review (review_author) can't POST pictures in StarterPics. So only the User that creates the review can post pictures on it.
I've tried another validation with no luck either:
def validate_restaurant_review_id(self, value):
if not RestaurantReview.objects.filter(restaurant_review_id=value,
review_author=self.request.user).exists():
raise serializers.ValidationError('Not your review')
return value
You could provide extra context to the serializer in addition to the object being serialized by passing a context argument when instantiating the serializer in your view.
serializer = RandomSerializer(instance, context={'request': request})
If you use Generic Views or ModelViewSet(inherited form GenericAPIView), then request is already available in your serializer self.context dict
class StarterPicsSerializer(serializers.ModelSerializer):
class Meta:
model = StarterPics
fields = '__all__'
def validate_restaurant_review_id(self, value):
print(self.context['request'])