I have an entity model, and i want to list all the instances of Entity based on the id of a category instance in it. So basically i'd like to list all the Entity instances with category id of 1 with /specialists/category/1 but for some reason i get
Not Found: /api/v1/specialists/categories/1
Even though the code looks alright.
class Entity(models.Model):
entity_category = models.ManyToManyField(Category)
class SpecialistSerializer(serializers.ModelSerializer):
entity_category = SpecialistCategorySerializer(read_only=True, many=True)
class Meta:
model = Entity
fields = (....., entity_category)
class SpecialistsPerCategory(generics.ListAPIView):
serializer_class = SpecialistSerializer
def get_queryset(self):
category_id = self.kwargs['pk']
return Entity.objects.filter(entity_category=category_id, lookup_type='in')
path('specialists/category/<int:pk>', SpecialistsPerCategory.as_view()),
Do you have any idea what's wrong?
You have category in path, but plural categories in url.
Also I think that will not work, because SpecialistSerializer have model of Entity, but you want to get pk of Category in SpecialistsPerCategory that is based on that serializer.
Related
I want to provide a custom queryset within a model admin class that inherits from TabluarInline, but I want to provide this queryset by calling a method of current instance of the model object.
I have two models. One for tracks belonging to an album, and one for the Album itself. Some tracks can be hidden and I have a method in Album to return only the visible tracks.
class Track(models.Model):
name = models.CharField()
length = models.IntegerField()
album = ForeignKey(Album)
hidden = BooleanField()
class Album(models.Model):
name = models.CharField()
def get_visible_tracks_queryset(self):
return self.track_set.filter(hidden=False)
And I have a tracks inline admin which is included on the django admin page for an album. I want to re-use the get_visible_tracks_queryset to define the queryset for this inline admin, I don't want to repeat the logic again. I can't figure out how to do it. I could do something like the following, however I'm using a simplified example here, I actually have more complex logic and I don't want to be repeating the logic in multiple places.
class TracksInlineAdmin(admin.TabularInline):
fields = ("name", "length")
model = Track
def get_queryset(self, request):
qs = super(TracksInlineAdmin, self).get_queryset(request)
return qs.filter(hidden=False)
Ideally I could do something like:
class TracksInlineAdmin(admin.TabularInline):
fields = ("name", "length")
model = Track
def get_queryset(self, request, parent_model_instance):
return parent_model_instance.get_visible_tracks_queryset()
Any thoughts on how to achieve this?
The cleanest way is to define a custom QuerySet class for your model in which you can define any complex filters for re-use in various places:
class Track(models.Model):
# fields defined here
objects = TrackManager()
class TrackManager(models.Manager):
def get_queryset(self):
return TrackQuerySet(self.model, using=self._db)
class TrackQuerySet(models.QuerySet):
def visible(self):
return self.filter(hidden=False)
Now, anywhere in code, when you have a queryset of tracks (e.g. Track.objects.filter(name="my movie")) you can add .visible() to filter further. Also on a related set:
album.track_set.all().visible()
I have a somewhat complex relationship between multiple models. A simplified example:
class Country(models.Model):
name = models.CharField([...])
[...]
def __ str__(self):
return f'{self.name}'
class Region(models.Model):
country = models.ForeignKey(Country)
name = models.CharField([...])
[...]
def __ str__(self):
return f'{self.name}'
class CityManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('region', 'region__country')
class City(models.Model):
name = models.CharField([...])
region = models.ForeignKey(Region)
objects = CityManager()
def __str__(self):
return f'{self.region.country} - {self.region} - {self.name}'
Hence when I want to display some kind of list of cities (e.g. list all cities in Germany), I have to use select_related to be even remotely efficient otherwise I query for Country each time the __str__ is called. This is not the problem.
The problem is that when I have unrelated group of models and I want to FK to City, such as:
class Tour(models.Model):
[...]
class TourItem(models.Model):
tour = models.ForeignKey(Tour)
city = models.ForeignKey(City)
[...]
Tour would represent a planned tour for some music band; and TourItem would be a specific tour in a given city. I have a simple admin interface for this, so that TourItem is an inline field for the Tour (ie. so multiple tour items can be edited/added simultaneously). The problem is that now there are multiple queries firing for same Country when looking up the City FK and I'm not sure how to solve it. I tried what follows, but it did not work as expected:
class TourManager(models.Manager):
def get_queryset(self):
return super().get_queryset().prefetch_related('touritem_set__city', 'touritem_set__city__region', 'touritem_set__city__region__country')
And neither did this work:
class TourItemManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('city', 'city__region', 'city__region__country')
How can I adjust the managers/models so that when I load Tour's admin there will not be additional queries fired for Country?
You can use ModelAdmin select_related to select related tables
list_select_related = ('author', 'category')
If that is not fully helpful and you still want to do override try with following in your custom Manager
def get_queryset(self, request):
return super(TourItemManager,self).queryset(request).select_related('city', 'city__region', 'city__region__country')
I have model with many links into it:
class Travel(BaseAbstractModel):
tags = models.ManyToManyField(
Tag,
related_name='travels',
)
owner = models.ForeignKey(
'users.TravelUser',
related_name='travel_owner'
)
payment = models.ForeignKey(
Payment,
related_name='travels',
)
country = models.ForeignKey(
Country,
related_name='travels,
)
........
Many of these models have only two fields with unique name and image.
I create serializer for each of these models and put them in TravelSerializer
class TravelBaseSerializer(DynamicFieldsModelSerializer):
owner = UserSerializer(required=False)
tags = TagSerializer(many=True)
payment = PaymentSerializer()
country = CountrySerializer()
Based on docs I override create() and update.
The problem is, when I sent JSON data, Django create each model from nested serializers. But I want to create only Travel instance. Also I want receive and respond serialized object not only pk field.
UPDATE
I solved this problem, put code in the answer. Now I can receive and respond with Serializer data without creating object.
But I think the DRF provides more elegant approach then I do. It is my first project with DRF, maybe I miss something and there's an easier solution.
I decide override to_internal_value() put it in custom serailizer and inherit all nested serializers from it:
class NestedRelatedSerializer(serializers.ModelSerializer):
def to_internal_value(self, data):
try:
pk = data['pk']
except (TypeError, KeyError):
# parse pk from request JSON
raise serializers.ValidationError({'_error': 'object must provide pk!'})
return pk
Get all pk from it and save in create and updated methods:
def update(self, instance, validated_data):
# If don't get instance from db, m2m field won't update immediately
# I don't understand why
instance = Travel.objects.get(pk=instance.pk)
instance.payment_id = validated_data.get('payment', instance.payment_id)
instance.country_id = validated_data.get('country', instance.country_id)
# update m2m links
instance.tags.clear()
instance.tags.add(*validated_data.get('tags'))
instance.save()
return instance
I'm not exactly sure I understand what you want to do, but could setting read_only_fields is the Meta class be what you need ?
class TravelBaseSerializer(DynamicFieldsModelSerializer):
owner = UserSerializer(required=False)
tags = TagSerializer(many=True)
payment = PaymentSerializer()
country = CountrySerializer()
class Meta:
read_only_fields = ('tags',)
See this section in the docs.
What i'm trying to do is to add a query result from a model to a modelresource, as you can see in this block of code:
def dehydrate(self, bundle):
bundle.data['image'] = place_image.image.get(place=1).get(cardinality=0)
I want to add a field to PlaceResource that will contain the image from place_site model where place=1 and cardinality=0. But im recieving an error:
The 'image' attribute can only be accessed from place_image instances
So, my question is: Is it impossible to use the query result from another model in a tastypie modelresource? Im sorry for my bad english, please correct me if something's wrong. Thanks for your time.
There's the complete code:
MODELS.py:
class place(models.Model):
idPlace = models.AutoField(primary_key=True)
Name = models.CharField(max_length=70)
class place_image(models.Model):
idImage = models.AutoField(primary_key=True)
place = models.ForeignKey(place,
to_field='idPlace')
image = ThumbnailerImageField(upload_to="place_images/", blank=True)
cardinality = models.IntegerField()
API.py
from models import place
from models import place_image
class PlaceResource(ModelResource):
class Meta:
queryset = place.objects.all()
resource_name = 'place'
filtering = {"name": ALL}
allowed_methods = ['get']
def dehydrate(self, bundle):
bundle.data['image'] = place_image.image.get(place=1).get(cardinality=0)
return bundle
class PlaceImageResource(ModelResource):
place = fields.ForeignKey(PlaceResource, 'place')
class Meta:
queryset = place_image.objects.all()
resource_name = 'placeimage'
filtering = {"place": ALL_WITH_RELATIONS}
allowed_methods = ['get']
The error you are getting is caused by the fact that you are accessing the image attribute of a model class, not instance.
The object that is being dehydrated in the dehydrate method is stored in obj attribute of the bundle parameter. Also, you are trying to filter place_image models to only those with place=1 and cardinality=0 by accessing the image attribute of place_image model class. Such filtering won't work as image is not a ModelManager instance. You should use objects attribute instead. Furthermore, get() method returns an actual model instance thus a subsequent call to get() will raise AtributeError as your place_image model instances have no attribute get.
So, all in all, your dehydrate should look like this:
def dehydrate(self, bundle):
bundle.data['image'] = place_image.objects.get(place_id=1, cardinality=0).image
return bundle
Notice that this code requires the place_image with desired values to exist, otherwise a place_image.DoesNotExist will be thrown.
There is also some redundancy in your models:
idPlace and idImage can be removed, as django by default creates an AutoField that is a primary key called id when no other primary key fields are defined
place_image.place field has a redundant to_field parameter, as by default ForeignKey points to a primary key field
I am new in django rest api developement. I have two models one is category and another is subcategories.
Here is my models
class Category(models.Model):
title = models.Charfield()
brief = models.TextField()
subcategories = model.ManyToManyField('Subcategory', blank=True)
My serializer class
class CategorySerializer(serializers.ModelSerializer):
title= serializer.Charfield()
subcategories = Relatedfield(many=True)
Now in view
def post(self, request, format = None):
data=request.DATA
serialize= CategorySerializer(data=request.DATA)
if serializer.valid():
serializer.save()
How to save nested data like {'title':"test",'subscategories':[{'description':'bla bla bla'},{'description':'test test'}]} in post method.
I have read this in documentation
Note: Nested serializers are only suitable for read-only
representations, as there are cases where they would have ambiguous or
non-obvious behavior if used when updating instances. For read-write
representations you should always use a flat representation, by using
one of the RelatedField subclasses.
Please let me suggest which is right way or solution to do nested relation post/put in django rest.
Have you tried creating a SubCategorySerializer and adding this as a field on CategorySerializer?
class SubcategorySerializer(serializers.ModelSerializer):
class Meta:
model = Subcategory
class CategorySerializer(serializers.ModelSerializer):
subcategories = SubcategorySerializer(many=True)
Docs: http://django-rest-framework.org/api-guide/relations.html#nested-relationships