13

I have a model that I overrode the save method for, so that the save method can be passed in some data and auto-fill-in a field before saving. Here is my model:

class AccountModel(models.Model):

    account = models.ForeignKey(Account)

    def save(self, request=None, *args, **kwargs):
        if request:
            self.account = request.session['account']
        super(AccountModel, self).save(*args, **kwargs)

    class Meta:
        abstract = True

The idea is I set up a base model for objects that need to be associated with an account and then I won't have to deal with the account connections every time they come up (which is a lot).

But: I'd also like to use get_or_create, which saves the new objects without passing in the request. I know it is doable to not use get_or_create and do a try/except instead, but I'd like to know if there is a way to override get_or_create and what is the proper way to do it.

I looked at the code for the Manager (which I am looking at overriding) and the get_or_create function just calls a QuerySet.get_or_create function. Maybe I can write it to use other manager functions and not the QuerySet version of get_or_create? What do y'all think?

Lutz Prechelt
  • 36,608
  • 11
  • 63
  • 88
lovefaithswing
  • 1,510
  • 1
  • 21
  • 37

1 Answers1

19

You could subclass django.db.models.query.QuerySet and override the get_or_create method there to accept your request keyword argument and pass it onto save I guess, but it isn't very pretty.

class AccountQuerySet(models.query.QuerySet):
    def get_or_create(...):
        ...

You could then add a custom manager to your Account model which uses this custom QuerySet:

class AccountManager(models.Manager):
    def get_queryset(self):
        return AccountQuerySet(self.model)

Then use this manager in your model:

class Account(models.Model):
    ...
    objects = AccountManager()

But you might find that the try-except method is neater after all :)

Lutz Prechelt
  • 36,608
  • 11
  • 63
  • 88
DrMeers
  • 4,117
  • 2
  • 36
  • 38
  • This helped me when I needed to override bulk_create(). Thanks! – Chad Jul 04 '13 at 07:26
  • This kept me from using Django internals when trying to override/overwrite/redefine `update_or_create`. – Lutz Prechelt Sep 14 '18 at 09:06
  • 3
    _"isn't very pretty"_? I think yours _is_ a neat solution. Without it, you need to keep a discipline of never using `get_or_create` anywhere for this particular model, which sounds dangerous to me. – Lutz Prechelt Sep 14 '18 at 09:08
  • looking for something similar then i found this post. even though get or create is pretty neat and sweet to use try - except might be neat in some cases. – Martins Oct 06 '18 at 23:52
  • 1
    `get_or_create` calls `create` internally, so if you override `create` directly it'll fix `get_or_create`, `update_or_create`, etc. – Arion Nov 05 '18 at 19:29