subclassing models.Manager - python

I see no difference in sub classing the models.manager object and overriding the get_query_set method or simply creating a new method in the sub class and using the method. For the reason being I have taken example from the django book;
class MaleManager(models.Manager):
def get_query_set(self):
return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager):
def get_query_set(self):
return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
With this I could use; Person.women.all() or Person.men.all() to fetch all the men or women model object. But, I guess the similar thing can be achieved without overriding the get_query_set method by simply doing;
class MaleManager(models.Manager):
def get_male(self):
return self.filter(sex='M')
class FemaleManager(models.Manager):
def get_female(self):
return return self.filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
Now, with this, I can fetch all those objects with a little technique like;
Person.objects.get_male() or Person.objects.get_female(). But, there's no any subtle difference between how I could fetch the objects yet there is difference in terms of readability and use in first case while the second one is much easier to understand and has lesser code.Do they make some significant difference in coding and patterns ? The other thing with the second one, what if I place both the methods inside a same class like;
class PeopleManager(models.Manager):
def get_male(self):
return self.filter(sex='M')
def get_female(self):
return return self.filter(sex='F')

Usually you don't want several managers for the model. It's better to extend default manager.
class PeopleManager(models.Manager):
def get_male(self):
return self.filter(sex='M')
def get_female(self):
return return self.filter(sex='F')
class Person(models.Model):
....
objects = PeopleManager()
Then you will be able to use Person.objects.get_male(), Person.objects.get_female() and built-in methods like Person.objects.order_by(). You can look at custom managers in django.contrib.auth.models for example.
get_query_set is good for inheritance. For example you can define
class SmithManager(PeopleManager):
def get_queryset(self):
return super(SmithManager, self).get_query_set().filter(last_name='Smith')
and all the methods of the manager will return only Smiths, (Person.objects.get_male() will return only males named Smith and so on). And you don't need to rewrite all the methods.

Related

Change representation of Superclass without field depending of Subclass object (__str__)

I am running a django app and have a setup like this:
ModelSuper(models.Model):
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name
So ModelForeign has a FK to ModelSuper. What happens now is that when I create an instance of ModelForeign I can choose if it belongs either to ModelSub1 or to ModelSub2. But the string representation is ModelSuper Onject (3) where (3) is the id.
Normally I can change this representation by overwriting the __str__ method on the model, but since I do not have any fields on the Supermodel I can't return anything.
What I tried:
I have already implemented the __str__ method in the Submodels but that does not help.
I wanted to make the Super model abstract. But this does not let me point FKs to the Supermodel, so I can't do this. My setup requires this FK
I used a generic FK with django's ContentType framework. This is also not an option because it messes completely with my app and is also not recommended from an SQL perspective.
Also when I do API-calls I get ModelSuper Onject (3) back instead of a human-readable name.
Is there a way to do what I intend to do? Thanks in advance for help and hints. Very much appreciated!
EDIT1: What I tried thanks to Abdul's help:
class ModelA(models.Model):
class Meta:
abstract = False
TYPE_CHOICES = [('sub1', 'sub1'), ('sub2', 'sub2')]
type_model = models.CharField(max_length=50, choices=TYPE_CHOICES, null=True, blank=True)
def __str__(self):
if self.type_model == "sub1":
return "sub1"
elif self.type_model == "sub2":
return "sub2"
else:
return "unkown"
I am not understanding how your foreign key works as model inheritance means the tables are separate. How about trying something like this:-
ModelA(models.Model):
TYPE_CHOICES = [('Sub1', 'ModelSub1'), ('Sub2', 'ModelSub2')]
model_type = models.CharField(max_length=4, choices=TYPE_CHOICES)
def __str__:
# Return string representation using if-else
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name

Django: How to set a property in a model using #property

I'm trying to assign two properties to my User class. The first assigned property will be used in assigning the second property. Is this correct? I'm using the #property decorator...
class User(n):
group = models.ForeignKey(Brand, null=True, blank=True)
is_admin = models.BooleanField(default=True)
# assign the apps property (User.apps)
#property
def assign_apps(self):
self.apps = get_user_apps(self.group, self.is_admin)
# with User.apps, assign the apps_meta property (User.apps_meta)
#property
def assign_apps_meta(self):
self.apps_meta = get_user_apps_meta(self.apps)
I still don't understand why you want to use properties here. This really isn't what they are for. The property decorator is for creating getters and setters for complex attributes; that's not what you're doing at all.
This seems like a job for a simple method.
def assign_apps(self):
self.apps = get_user_apps(self.group, self.is_admin)
self.apps_meta = get_user_apps_meta(self.apps)
and now you can call my_user.assign_apps().
if you're writing method that returns some value or string then you should use
#property decorator
class Person(models.Model):
name = models.CharField(max_length=255)
birth_date = models.DateField()
#property
def age(self):
return ......

django model referencing object from other class

Hi there pretty new to django but considering the below models, with their relationships, how do I create a read only field for the object that is a reference to a field in another class? I've looked for a while on stackoverflow, but not sure what kind of model reference that would be.
The basic logic for this being:
I have this server rack that sites on a floor in a server room, and I'm associating it to a rack position, and row to manage power consumption and other goodies. Just for my end-user's reference I want a read only field to show them what row this rack lives in, and its derived from the rack position. I'd been fiddling around with creating a method to look it up, but can't seem to figure out the syntax or find something related on the django admin pages.
Any ideas would be super appreciated, I really could use the help as I've been staring through docs forever, and can't seem to find a relevant model reference for this.
class rack(models.Model):
class Meta:
verbose_name = "Rack"
verbose_name_plural = "Racks"
def __unicode__(self):
return str(self.position)
def row(self, obj):
return self.position.row
position = models.OneToOneField("rackposition")
row = row(position.row.row)
asstag = models.CharField("Asset Tag", max_length=200, unique=True)
rackunits = models.IntegerField("Rack Units")
class rackposition(models.Model):
class Meta:
verbose_name = "Rack Position"
verbose_name_plural = "Rack Positions"
def __unicode__(self):
return str(self.position)
position = models.CharField("Position", max_length=35, primary_key=True)
row = models.ForeignKey("row")
class row(models.Model):
class Meta:
verbose_name = "Row"
verbose_name_plural = "Rows"
def __unicode__(self):
return str(self.row) + "." + str(self.suite)
row = models.CharField("Row ID", max_length=200, unique=True)
suite = models.ForeignKey(suite, blank=False)
power_budget = models.IntegerField("Power Budget")
power_volt = models.IntegerField("Power Voltage")
dual_bus = models.BooleanField("Dual Bus", default=False)
You don't need a method. Assuming you have a rack instance called my_rack, you can get its row with my_rack.position.row.
Note, you should really follow PEP8 and use CamelCase for your class names.
If you want to see it as a readonly field in the admin, you will need to define a method either on the model or on the ModelAdmin class. For example:
class RackAdmin(admin.ModelAdmin):
model = Rack
readonly_fields = ('row',)
def row(self, obj):
return obj.position.row

Relationships with pairs of interrelated models

Let's say I have 3 models in Django: Person, Workfield and SubWorkfield.
A person can have many workfield-s and many subWorkfield-s as well, but the subWorkfield-s must be related to their parent workfield-s ( which the person must be related to).
So how do I enforce that whenever a person is related to a subWorkfield then he must also be related to that subWorkfield's parent workfield?
Here's what I have so far, but I don't think it enforces the relationship:
class Person(models.Model):
name = models.CharField(max_length=200)
workfield = models.ManyToManyField(Workfield)
subworkfield = models.ManyToManyField(SubWorkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
I need to have the workfields and subWorkfield-s decoupled because a person can belong to a workfield without any subWorkfield-s.
There really isn't any good way to do what you want purely with the table structure itself. However, if you do the following:
class Person(models.Model):
name = models.CharField(max_length=200)
workfields = models.ManyToManyField(Workfield)
subworkfields = models.ManyToManyField(SubWorkfield)
def add_subworkfield(self, subworkfield):
if subworkfield.workfield not in self.workfields:
return False
else:
self.subworkfields.append(subworkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
And then you will just use the add_subworkfield method when adding subworkfields to Persons.
Alternatively, inside of the Person model you could override the save method:
class Person(models.Model):
...
def save(self, *args, **kwargs):
for subworkfield in self.subworkfields:
if subworkfield.workfield not in self.workfields:
return
super(Person, self).save(*args, **kwargs)
If it's a small app and it's just you, the first way should suffice. However, if you're working on something larger with multiple people, overriding save would be a safety incase someone doesn't use the add_subworkfield method.

django - a model field which gets the result of model method

I have this model
class Home(models.Model):
...
number_female = models.IntegerField()
number_male = models.IntegerField()
def all_people(self):
return self.number_female + self.number_male
all_members = all_people(self)
i am getting: name 'self' is not defined.
how can I define a field which gets the result of models method? this Home scenario is just an example, i have more complex models, i just wanted to make question clearer.
If you would like to add a calculated field like all_members as a part of your model, then you will have to override the save function:
class Home(models.Model):
...
all_members = models.IntegerField()
def save(self):
all_members = self.all_people()
super(Home, self).save()
Now you can filter by all_members. It would be better to use the #property decorator for all_members, in this case.
Another approach would be to use Django's extra method as mentioned in a different stackoverflow answer
You still need to define all_members as a model field (not as an integer), and then populate it with the desired value when you save() the instance.
class Home(models.Model):
...
number_female = models.IntegerField()
number_male = models.IntegerField()
all_members = models.IntegerField()
def save(self):
self.all_members = self.number_female + self.number_male
super(Home, self).save()
I think Django Managers can be a solution here. Example:
Custom Manager:
class CustomFilter(models.Manager):
def all_people(self):
return self.number_female + self.number_male
Model:
class Home(models.Model):
....
objects= CustomFilter()
Views:
allpeople= Home.objects.all_people(Home.objects.all())

Categories