How to respond with child object based on its parent id? - python

I have a parent class and a child class, like so:
class Animal(models.Model):
age = models.IntegerField()
class Bird(Animal)
wingSpan = models.DecimalField()
Here's the view to get birds:
class BirdViewSet(viewsets.ModelViewSet):
queryset = Bird.objects.all()
serializer_class = BirdSerializer
And here is the serializer:
class BirdSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Bird
fields = ['age', 'wingSpan']
In the database, jango appropriately created an animal_prt_id field in the birds table, so that this view/serializer pair knows where to find the age, which is mapped to the parent class, Animal.
How to make a view/serializer pair that does the opposite, meaning, it receives the id of an Animal and responds with the complete Bird (or with any other Animal subclass I might have)?

There is an useful package called django-model-utils that provides an InheritanceManager logic.
TLDR: After setting up the package, your code will sort of look like this
bird = Animal.objects.filter(pk=animal_id).select_subclasses("bird")

Related

Django ORM: add parent class and access its class variable from children

I am working on a restaurant app (and new to Django/Python). I want to have a parent class Dish that will contain some counter or ID that increments for every instance of a child class of Dish. The instances are dishes like Pizza, Pasta, etc with different characteristics. I've tried making Dish abstract and non-abstract, but come across different issues each time.
This is my Dish class (to make it abstract I tried InheritanceManager(), but ran into complications there that led me to think it's overkill for my simple purposes. Non-abstract, kept giving me You are trying to add a non-nullable field 'pasta_ptr', followd by IntegrityError: UNIQUE constraint failed):
class Dish(models.Model):
#objects = InheritanceManager()
counter = models.PositiveIntegerField(default=0)
class Meta:
abstract = True
This is an example of a child class - I'd like every pasta-entry to get its own Dish-ID or counter on the menu - like a class attribute in Python. How do I access and implement this from the child class? If Dish is not abstract, can I use (& access) Dish's primary key that will tie each dish to my desired ID?
class Pasta(Dish):
#Dish.counter +=1
name = models.CharField(max_length=64, primary_key=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return f"{self.name}, price: ${self.price}"

Create resource via POST specifying related field ID

I am using Django Rest Framework, and want to allow API clients to create resources, where one attribute of the created resource is the (required) primary key of a related data structure. For example, given these models:
class Breed(models.Model):
breed_name = models.CharField(max_length=255)
class Dog(models.Model):
name = models.CharField(max_length=255)
breed = models.ForeignKey(Breed)
I want to allow the caller to create a Dog object by specifying a name and a breed_id corresponding to the primary key of an existing Breed.
I'd like to use HyperlinkedModelSerializer in general for my APIs. This complicates things slightly because it (apparently) expects related fields to be specified by URL rather than primary key.
I've come up with the following solution, using PrimaryKeyRelatedField, that behaves as I'd like:
class BreedSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Breed
class DogSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Dog
read_only_fields = ('breed', )
breed_id = serializers.PrimaryKeyRelatedField(queryset=Breed.objects.all())
def create(self, validated_data):
validated_data['breed'] = validated_data['breed_id']
del validated_data['breed_id']
return Dog.objects.create(**validated_data)
But it seems weird that I would need to do this mucking around with the overloaded create. Is there a cleaner solution to this?
Thanks to dukebody for suggesting implementing a custom related field to allow an attribute to be serialized OUT as a hyperlink, but IN as a primary key:
class HybridPrimaryKeyRelatedField(serializers.HyperlinkedRelatedField):
"""Serializes out as hyperlink, in as primary key"""
def to_internal_value(self, data):
return self.get_queryset().get(pk=data)
This lets me do away with the create override, the read_only_fields decorator, and the weirdness of swapping out the breed and breed_id:
class BreedSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Breed
class DogSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Dog
breed = HybridPrimaryKeyRelatedField(queryset=Breed.objects,
view_name='breed-detail')

Correctly using subclasses/proxies in django that have self referential foreign keys?

I have two classes, with a super class. In essence the two classes are concrete classes on a tree. One is a leaf, one is a branch. They share properties defined in the super class.
None of the below classes are finished. I've tried both making the superclass abstract, and the subclasses proxies. Hopefully the code below explains what I'm trying to achieve.
This is the 'super class'
class Owner(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class Meta:
abstract=True
This is the 'leaf'
class User(Owner):
pass
This is the 'branch'.
class Group(Owner):
head = models.ForeignKey(User)
members = models.ManyToManyField(Owner,through='Membership')
This shows how a user can belong to a group by a membership.
class Membership(models.Model):
date_joined = models.DateField()
user = models.ForeignKey(Owner)
group = models.ForeignKey(Group)
My restrictions are that each user can belong to many groups (via the linker Membership). Each group can be a member of a single group.
This fails because I'm referencing Owner in both the membership as the user, and in the group members. I feel like this is the sort of thing I could solve with generics in Java, but thats not going to help me here.
I've also seen ContentTypes used for this sort of thing, but they seem too complicated for what I'm trying to do. Am I wrong? I can't figure out how to apply that paradigm to my example. I also found this question but I'm still not certain on how it should be implemented.
You can't have foreign key fields pointing to an abstract class (there is no table in the DB for an abstract class).
You'll probably need to implement self-referential foreign key for each Group to belong to zero or one group. Something like this:
class Base(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class Meta:
abstract=True
class User(Base):
groups = models.ManyToManyField('Group', through='Membership', related_name='members')
class Group(Base):
head = models.ForeignKey(User)
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
def descendants(self, **kwargs):
qs = self.children_set.filter(**kwargs)
for group in self.children_set.all():
qs = qs | group.descendants(**kwargs)
return qs
class Membership(models.Model):
date_joined = models.DateField()
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
The Base class above does nothing other than dictate that each inherited class has a name field in their respective DB table and __unicode__ method- nothing more. You could simply copy and paste the name field and __unicode__ method into User and Group and it would be functionally identical. Having a common abstract parent doesn't create any DB relationships and you can't use it to query the DB.
I can't really see any reason for using proxy classes in this situation.

I want to access a relationship through an inherited class

See the contrived example below. If I have an Owner and I want to access all their Cars or all their Bicycles, I can't think of a clean way to do that. Please note that I can't make Vehicle an abstract class, since the VehicleIncident class is using it in a FK relationship.
from django.db import models
from foo import Owner
class Vehicle(models.Model):
owner = models.ForeignKey(Owner)
class VehicleIncident(models.Model):
vehicle = models.ForeignKey(Vehicle)
class Car(Vehicle):
steering_wheel = models.CarPartField()
class Bicycle(Vehicle):
handlebars = models.HandlebarField()
If I know the owner (User), how can I get a specific vehicle type?
This throws an error:
object has no attribute
owner.bicycle_set.all()

Object has no attribute _state

I'm developing Django application, and I have following error
'Sheep' object has no attribute _state
My models are constructed like this
class Animal(models.Model):
aul = models.ForeignKey(Aul)
weight = models.IntegerField()
quality = models.IntegerField()
age = models.IntegerField()
def __init__(self,aul):
self.aul=aul
self.weight=3
self.quality=10
self.age=0
def __str__(self):
return self.age
class Sheep(Animal):
wool = models.IntegerField()
def __init__(self,aul):
Animal.__init__(self,aul)
What I must do?
firstly, you must be very careful overriding __init__ to have non-optional arguments. remember it will be called every time you get an object from a queryset!
this is the correct code you want:
class Animal(models.Model):
#class Meta: #uncomment this for an abstract class
# abstract = True
aul = models.ForeignKey(Aul)
weight = models.IntegerField(default=3)
quality = models.IntegerField(default=10)
age = models.IntegerField(default=0)
def __unicode__(self):
return self.age
class Sheep(Animal):
wool = models.IntegerField()
I highly suggest setting the abstract option on Animal if you will only ever be using subclasses of this object. This ensures a table is not created for animal and only for Sheep (etc..). if abstract is not set, then an Animal table will be created and the Sheep class will be given it's own table and an automatic 'animal' field which will be a foreign key to the Animal model.
Django docs recommend against you to use __init__ method in models:
You may be tempted to customize the model by overriding the __init__ method. If you do so, however, take care not to change the calling signature as any change may prevent the model instance from being saved. Rather than overriding __init__, try using one of these approaches:
Add a classmethod on the model class
Add a method on a custom manager (usually preferred)

Categories

Resources