I want to access a relationship through an inherited class - python

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()

Related

Is it possible to set the related_name attribute for a field from an abstract class to an attribute of the concrete class in Django?

In Django, I have an abstract class:
class AbstractResult(models.Model):
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, related_name='%(class)s')
...
class Meta:
abstract = True
This gives me some control over the related name of this field for each concrete class, but not full control. I'd like to be able to set this name in each concrete class.
I thought maybe I could use a callable:
class AbstractResult(models.Model):
def get_related_name(self):
raise NotImplementedError("Override this method in your concrete model classes!")
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, related_name=get_related_name)
But that didn't work, as the related_name arg doesn't take a callable.
I also thought maybe I could do:
class AbstractResult(models.Model):
related_name = 'OVERRIDE THIS ATTRIBUTE!'
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, related_name=related_name)
and then try to use it in the concrete class:
class TestA_Result(AbstractResult):
related_name = "test_a_results"
but that didn't work either.
Why didn't that work? Is there any simple way to make it work?
Update:
It looks like the "problem" is that the related_name=related_name in my last example gets evaluated when the AbstractResult class is being constructed - not when the TestA_Result class is being constructed. This isn't a Django thing - this is just how Python works:
class AbstractClass:
field_one = 'field one set in abstract class'
field_two = field_one
class ConcreteClass(AbstractClass):
field_one = 'set in concrete class'
inst = ConcreteClass()
inst.field_one # 'set in concrete class'
inst.field_two # 'field one set in abstract class'
Is there a way around this that would allow me to set the related_name in Django's concrete class?

Access related set from a Custom QuerySet in Django?

I want to annotate additional field to my model via custom queryset and access it with a function, here is a simplified code:
class CustomQuerySet(models.QuerySet):
def my_func(self):
return self.annotate(b_count=self.b_set.count()) # 'CustomQuerySet' object has no attribute 'b_set'
class A(models.Model):
objects = CustomQuerySet.as_manager()
class B(models.Model):
a = models.ForeignKey(A)
I want to access it like this models.A.objects.my_func().all(), so an extra field would be added to my model when I want to get it.
But I can't access b_set from a CustomQuerySet.
Previously I was using a #property in model A, but it makes an additional DB request every time.
How could I access a set of related model from a Custom QuerySet?
Probably, you should take a look at the Manager example that does exactly what you want: https://docs.djangoproject.com/en/3.2/topics/db/managers/#adding-extra-manager-methods
from django.db import models
from django.db.models.functions import Coalesce
class PollManager(models.Manager):
def with_counts(self):
return self.annotate(
num_responses=Coalesce(models.Count("response"), 0)
)
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
# ...
In your example, I think you can't access b_set because this attribute belongs to a model instance, not to the queryset itself.

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

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")

How to remove unique constraint for a model attribute of an abstract model after inheritance?

I have an abstract class as:
class AbstractExecutive(models.Model):
mobile = models.CharField(max_length=10,unique=True,
verbose_name='*Mobile')
#other attributs not required....
class Meta:
abstract = True
I am inheriting this class to create different instances like Client,Vendor etc. For a class instance Client, I require that the unique constraint is dropped, while it exists for other class objects. I am using postgresql 9.1 I I dropped the the client table constraint using psql, but since the model is inherited, it always has unique constraint on it. Please note the Client table has data and cannot be disturbed. How can I get rid of the constraint in the table. Client class model:
class Client(AbstractAddress,AbstractExecutive):
number = models.CharField(max_length=10,verbose_name='number',
unique=True)
#other attributes...
You could try to override the inherited mobile field of Client:
class Client(...):
...
Client._meta.get_field('mobile')._unique = False
Unfortuanately this is not possible in django (https://docs.djangoproject.com/en/dev/topics/db/models/#field-name-hiding-is-not-permitted). You need to remove mobile from your abstract class and put it to the concrete classes (either with or without unique).
Maybe it wasn't possible with old Django versions, but now the solution is to re-define the attribute on the child class, without the unique=True constraint.
So in the given example, with the unique mobile attribute in the AbstractExecutive abstract model and that must not be unique in Client model:
Your abstract class:
class AbstractExecutive(models.Model):
mobile = models.CharField(max_length=10, unique=True,
verbose_name='*Mobile')
#other attributs not required....
class Meta:
abstract = True
The child non-abstract class:
class Client(AbstractAddress, AbstractExecutive):
mobile = models.CharField(max_length=10, verbose_name='*Mobile')
#other attributes...
When generating the migrations, the unique constraint will be removed in Client model.

django abstract models versus regular inheritance

Besides the syntax, what's the difference between using a django abstract model and using plain Python inheritance with django models? Pros and cons?
UPDATE: I think my question was misunderstood and I received responses for the difference between an abstract model and a class that inherits from django.db.models.Model. I actually want to know the difference between a model class that inherits from a django abstract class (Meta: abstract = True) and a plain Python class that inherits from say, 'object' (and not models.Model).
Here is an example:
class User(object):
first_name = models.CharField(..
def get_username(self):
return self.username
class User(models.Model):
first_name = models.CharField(...
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(...
I actually want to know the difference between a model class that
inherits from a django abstract class (Meta: abstract = True) and a
plain Python class that inherits from say, 'object' (and not
models.Model).
Django will only generate tables for subclasses of models.Model, so the former...
class User(models.Model):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(max_length=255)
...will cause a single table to be generated, along the lines of...
CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
...whereas the latter...
class User(object):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Employee(User):
title = models.CharField(max_length=255)
...won't cause any tables to be generated.
You could use multiple inheritance to do something like this...
class User(object):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Employee(User, models.Model):
title = models.CharField(max_length=255)
...which would create a table, but it will ignore the fields defined in the User class, so you'll end up with a table like this...
CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
An abstract model creates a table with the entire set of columns for each subchild, whereas using "plain" Python inheritance creates a set of linked tables (aka "multi-table inheritance"). Consider the case in which you have two models:
class Vehicle(models.Model):
num_wheels = models.PositiveIntegerField()
class Car(Vehicle):
make = models.CharField(…)
year = models.PositiveIntegerField()
If Vehicle is an abstract model, you'll have a single table:
app_car:
| id | num_wheels | make | year
However, if you use plain Python inheritance, you'll have two tables:
app_vehicle:
| id | num_wheels
app_car:
| id | vehicle_id | make | model
Where vehicle_id is a link to a row in app_vehicle that would also have the number of wheels for the car.
Now, Django will put this together nicely in object form so you can access num_wheels as an attribute on Car, but the underlying representation in the database will be different.
Update
To address your updated question, the difference between inheriting from a Django abstract class and inheriting from Python's object is that the former is treated as a database object (so tables for it are synced to the database) and it has the behavior of a Model. Inheriting from a plain Python object gives the class (and its subclasses) none of those qualities.
The main difference is how the databases tables for the models are created.
If you use inheritance without abstract = True Django will create a separate table for both the parent and the child model which hold the fields defined in each model.
If you use abstract = True for the base class Django will only create a table for the classes that inherit from the base class - no matter if the fields are defined in the base class or the inheriting class.
Pros and cons depend on the architecture of your application.
Given the following example models:
class Publishable(models.Model):
title = models.CharField(...)
date = models.DateField(....)
class Meta:
# abstract = True
class BlogEntry(Publishable):
text = models.TextField()
class Image(Publishable):
image = models.ImageField(...)
If the Publishable class is not abstract Django will create a table for publishables with the columns title and date and separate tables for BlogEntry and Image. The advantage of this solution would be that you are able to query across all publishables for fields defined in the base model, no matter if they are blog entries or images. But therefore Django will have to do joins if you e.g. do queries for images...
If making Publishable abstract = True Django will not create a table for Publishable, but only for blog entries and images, containing all fields (also the inherited ones). This would be handy because no joins would be needed to an operation such as get.
Also see Django's documentation on model inheritance.
Just wanted to add something which I haven't seen in other answers.
Unlike with python classes, field name hiding is not permited with model inheritance.
For example, I have experimented issues with an use case as follows:
I had a model inheriting from django's auth PermissionMixin:
class PermissionsMixin(models.Model):
"""
A mixin class that adds the fields and methods necessary to support
Django's Group and Permission model using the ModelBackend.
"""
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text='Specific permissions for this user.')
class Meta:
abstract = True
# ...
Then I had my mixin which among other things I wanted it to override the related_name of the groups field. So it was more or less like this:
class WithManagedGroupMixin(object):
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
related_name="%(app_label)s_%(class)s",
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
I was using this 2 mixins as follows:
class Member(PermissionMixin, WithManagedGroupMixin):
pass
So yeah, I expected this to work but it didn't.
But the issue was more serious because the error I was getting wasn't pointing to the models at all, I had no idea of what was going wrong.
While trying to solve this I randomly decided to change my mixin and convert it to an abstract model mixin. The error changed to this:
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
As you can see, this error does explain what is going on.
This was a huge difference, in my opinion :)
The main difference is when you inherit the User class. One version will behave like a simple class, and the other will behave like a Django modeel.
If you inherit the base "object" version, your Employee class will just be a standard class, and first_name won't become part of a database table. You can't create a form or use any other Django features with it.
If you inherit the models.Model version, your Employee class will have all the methods of a Django Model, and it will inherit the first_name field as a database field that can be used in a form.
According to the documentation, an Abstract Model "provides a way to factor out common information at the Python level, whilst still only creating one database table per child model at the database level."
I will prefer the abstract class in most of the cases because it does not create a separate table and the ORM does not need to create joins in the database. And using abstract class is pretty simple in Django
class Vehicle(models.Model):
title = models.CharField(...)
Name = models.CharField(....)
class Meta:
abstract = True
class Car(Vehicle):
color = models.CharField()
class Bike(Vehicle):
feul_average = models.IntegerField(...)

Categories

Resources