Django: Use a model instance in another model's method
Let's say I have a model coconut:
class Coconut(models.Model):
carrier = models.ForeignKey('birds.Swallow')
husk_color = models.IntegerField(Choices=COLORS)
Now in some cases I want to set the carrier in the view or a management command or wherever.
In other cases, however, I'd like the default to be a 开发者_运维问答particular swallow.
I'm tempted to:
BERT_THE_AFRICAN_SWALLOW = Swallow.objects.get(id=7)
def set_carrier(swallow=BERT_THE_AFRICAN_SWALLOW):
self.carrier = swallow
Obviously this isn't right because it's coupled to the existing data in the database.
Also, it causes the test runner to bork if the "birds" app hasn't been instantiated yet.
So what is the right way?
Some examples of times when I run into this problem:
- Assigning an automated process to a "creator" user, which is actually a robot of sorts
- In VOIP apps, assigning particular processes to a specific, pre-defined PhoneNumber object
- In custom authorization scenarios, combining pre-defined privileges or Permission objects.
The way I understand it, what you actually want to do is to configure the default object of some sort. You can create a config app for that, so that you can do the configuration in the admin interface, or store it in a fixture or something.
A model that maps a string (the configuration option) to an object of any type would do. The contenttype app perfectly suits the kind of foreign keys you need for that.
# models.py
from django.db import models
from django.db.contenttypes.models import ContentType
from django.db.contenttypes import generic
class ConfigurationOption(models.Model):
name = models.SlugField(max_length = 255, unique=True)
value_id = models.PositiveIntegerField()
value_type = models.ForeignKey(ContentType)
value = generic.GenericForeignKey('value_type', 'value_id')
class Meta:
unique_together = 'value_id', 'value_type'
You then also depend on data to exist in the configuration app, but now that is made explicit.
An option can be to use model signals (the choice depends on when/where/how you need values to be there defaulted or overridden) and proxy models.
https://docs.djangoproject.com/en/dev/ref/signals/#module-django.db.models.signals
Using Django model inheritance you can create proxy models that have different post/pre methods and share the same table.
https://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models
class Coconut(models.Model):
carrier = models.ForeignKey('birds.Swallow')
husk_color = models.IntegerField(Choices=COLORS)
class CoconutCarriedByShallow(Coconut):
pass
class Meta():
proxy = True
cached_carriers_dict = {}
def assign_shallow_carrier(sender, instance, **kwargs):
import birds.models
attrs = {'name': 'Jhon', 'kind': 'super-fast'}
carrier = cached_carriers_dict.get(tuple(attrs.items()))
if carrier is None:
defaults = {'color': 'blue'}
carrier, c = birds.models.Swallow.objects.get_or_create(**attrs, defaults= defaults)
cached_carriers_dict.update({tuple(attrs.items()): carrier})
instance.carrier = carrier
models.signals.post_init.connect(assign_shallow_carrier, sender=CoconutCarriedByShallow)
Of course get_or_create is not always an available option, if not fixtures are another option you might want to consider.
hope it helps ;)
精彩评论