Using a Django Custom Manager - django

Can anyone help me create a custom manager that does the following....
This is just a learning exercise for me, it's not a real application so I'm after as much explanation as possible.
1) takes 2 objects, its self (Person) and Profile
2) divides person.age with profile.dog_years
3) adds this to model
My guess is that first I create a custom manager in my models.py
class PersonManager(models.Manager):
def make_score(self,profile):
Within the custom manager I then do the math self.age / profile.dog_years.
Then some how return it?
Add it to the model i.e. dogAge = PersonManager()
Outcome:
Then what I'm hoping to happen is that when I get all persons in my view
return Profile.objects.filter() (somehow pass profile here?) it has a new field called dogAge with the dog ages for all persons listed.

This isn't something that you'd do in a Manager. This is a job for a normal Model method.
Manager methods are for things that modify the query in some way. That's not what you want to do: you want to annotate a property to each object in the queryset. Since that doesn't require any further database calculations, a model method is the appropriate place.

Related

When to use a custom model manager?

Right now i'm reading about custom managers that you can use to add additional logic when doing a CRUD action like Create. You make a custom manager class and then initialize the objects attribute of the table class with an instance of the custom manager class.
I've also read that you can also use the pre_save and post_save signals to add additional save logic to the save action. My question is: When should i use a custom manager class over signals and is the use of a custom manager slower then the default manager of the Model class?
Thank you
OK, here's one I wrote earlier. I've cut out a lot of irrelevant stufff, hoping all the essentials remain. You need to know there's a Foreign Key chain PRSBlock -> PRS2 -> Jobline -> Description. Using this, all queries for PRSBlock.objects.filter(... will return PRSBlock objects with extra fields membrane_t, substrate_t and material from the Description found by following this chain. They're almost always needed in this context, unlike most of the other things along that chain. select_related would be serious overkill.
class PRSBlock_Manager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
membrane_t = F('PRS__jobline__description__mt'),
substrate_t= F('PRS__jobline__description__ft'),
material = F('PRS__jobline__description__material')
)
class PRSblock( models.Model):
# override the default manager
objects=PRSBlock_Manager() # annotate always with description parametrs relevant to block LUTs
PRS = models.ForeignKey( PRS2, models.CASCADE, related_name='prs_blocks')
# and lots of other fields that aren't relevant

What is the difference between creating a model instance using the default Manager and Customer Manager

While going through the official Django documentation I came across the Model Instance reference section in which it is mentioned that you can create an instance of the model using the custom Model using self.create. I wanted to know what's the difference between using the create method and the custom create_method when both are using the same fields and in both the cases the data is being saved in the DB.
Documentation:
https://docs.djangoproject.com/en/2.2/ref/models/instances/#creating-objects
class BookManager(models.Manager):
def create_book(self, title):
book = self.create(title=title)
return book
class Book(models.Model):
title = models.CharField(max_length=100)
objects = BookManager()
book = Book.objects.create_book("Pride and Prejudice")
Difference between these two
book2 = Book.objects.create(title="Pride and Prejudice")
Well in this simplest case, there is no difference. The reason of describing this technique in docs is obvious there
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:
It means you may be want to set some extra/default values to model instance. If you override constructor for this purpose, it is a little unsafe (and not usually a practice in django). That's why two other techniques for doing this are described. You are mentioning one of them. You can do some extra stuff in custom manager method if you want
class BookManager(models.Manager):
def create_book(self, title):
# you can do some extra stuff here for instance creation
book = self.create(title=title)
# or here when it is saved to db
return book
Otherwise there is no difference.

Django - Meta.base_manager_name - make related argument in the custom queryset and manager

I have a custom model manager and a custom queryset defined specifically for related obj which means I have defined Meta.base_manager_name in the model.
I would like to use a all() manager method which fetches related obj on a OneToOneFeild.
Now I know this does not make sense since OneToOneFeild will always return one obj there is no need for a all() method. I am working on django-oscar project and am extending its "Partner" model. It originally has a field "users" with ManyToManyField and now changed to a OneToOneFeild.
The users field is called in code several times using relation user.partners.all(). I don't want to extend/modify all these places (am I being lazy here?) since I want to keep the code as upgrade friendly as possible and so instead I wanted to have all() model manager defined which will work. Not sure if it is a good idea?
the all() method takes user arg to return queryset of the user instance
class PartnerQuerySet(models.QuerySet):
def all(self, user):
return self.filter(user=user)
class PartnerManager(models.Manager):
def get_queryset(self):
return PartnerQuerySet(self.model, using=self._db)
def all(self, user):
return self.get_queryset().all(users)
class Partner(models.Model):
objects = PartnerManager()
class Meta:
base_manager_name = 'objects'
The problem is when it is used with related obj it asks for user arg which makes sense but since I am using it with a related obj I wanted to use the related obj as arg so,
user.partner.all() - should use user as arg and fetch the results
user.partner.all(user) - and I should not have to do the below
2 related questions:
1) Does this make sense - should I be doing this?
2) how I can achieve user.partner.all() without adding user in arg
PS: I know i can work with middleware to get_current_user but this function is not reliable as per some of the responses on a different question on SO.
I don't think what you are trying to do will work. Your new situation with a OneToOneField gives you the partner instance.
>>>> user.partner
<Partner xxx>
While in the old situation with the ManyToManyField, the PartnerQuerySet would've been returned.
>>>> user.partner
<PartnerQuerySet []>
A solution would be to create a custom OneToOneField, but this would most probably violate the "simple is better than complex" rule and in the end may even be more work than changing all existing .all()'s.

How to modify Django's Manager QuerySet create() method?

I have two models, Invoice and InvoiceItems, which have a one-to-many relationship.
Throughout the code base we're creating InvoiceItems for a given Invoice using the Manager object as:
invoice.invoice_items.create(...)
The thing is, now we have a validation that has to take place before trying to create an InvoiceItem, and going through the codebase, refactoring all the creation pieces would be a headache.
I wonder if there's a way to override the create method itself or should we go for the model's save()?
To modify a Manager's method you need to create your own. Given the following case:
# models
class MyModel(models.Model):
# ... fields
objects = MyManager()
class MyManager(models.Manager):
def create(self):
# write your own code here
pass
Do not worry about the others methods (filter, delete, etc.) all of them will work as usual.
You can find more about custom managers here

Model Mommy Recipe with Reverse FK

I'm using model_mommy with Django tests to create objects. I'm having trouble creating a model with a reverse FK. I can do it the opposite way round as a workaround, but whilst it works it doesn't look right so I wonder if I can do it the other way round?
Say I have two models, User and Profile, related via an FK from Profile to User (it's not a one to one, it's just an FK). The Profile model has a bool attribute call is_aardvark.
In model mommy I can create recipes like so:
aardvark_profile = Recipe(Profile, is_aardvark=True)
non_aardvark_profile = Recipe(Profile, is_aardvark=False)
Then I can create a User with an aardvark profile in my test with something like:
user = mommy.create_recipe(aardvark_profile).user
This doesn't seem right, as I'm creating a user via the aardvark_profile recipe. I want to create a User via some sort of User recipe ideally (maybe in future I'll have some other model FKd to User, so the above wouldn't work).
I've tried things like the below, which doesn't work:
# doesn't work
broken_aardvark_user = Recipe(User, profile_set=mommy.create_recipe(aardvark_profile)
Is this even possible? Any ideas? I could just create a helper method to do this for me if all else fails.
Thanks!
You could do this:
from model_mommy.recipe import Recipe, related
aardvark_profile = Recipe(Profile, is_aardvark=True)
aardvark_user = Recipe(User, profile_set=related('aardvark_profile'))
Hope it helped
[1] http://model-mommy.readthedocs.org/en/latest/recipes.html#recipes-with-foreign-keys