50👍
Take a look at the AutoOneToOneField
in django-annoying. From the docs:
from annoying.fields import AutoOneToOneField
class MyProfile(models.Model):
user = AutoOneToOneField(User, primary_key=True)
home_page = models.URLField(max_length=255)
icq = models.CharField(max_length=255)
(django-annoying is a great little library that includes gems like the render_to decorator
and the get_object_or_None
and get_config
functions)
35👍
Like m000 pointed out:
… The catch in the question is that the models belong to different apps. This matches the use case for signals: "allow decoupled applications get notified when actions occur elsewhere in the framework". Other proposed solutions work but introduce an unnecessary A->B dependency, essentially bundling the two apps. Signals allows A to remain decoupled from B.
Your models exist in different apps. Often you use apps you didn’t write, so to allow updates you need a decoupled way to create logically related models. This is the preferred solution in my opinion and we use it in a very large project.
By using signals:
In your models.py
:
from django.db.models import signals
def create_model_b(sender, instance, created, **kwargs):
"""Create ModelB for every new ModelA."""
if created:
ModelB.objects.create(thing=instance)
signals.post_save.connect(create_model_b, sender=ModelA, weak=False,
dispatch_uid='models.create_model_b')
You can create a separate app to hold this models.py file if both of the apps are off-the-shelf.
- [Django]-How to filter objects for count annotation in Django?
- [Django]-ImproperlyConfiguredError about app_name when using namespace in include()
- [Django]-Remove default apps from Django-admin
15👍
The most straightforward way is to override the save method of ModelA:
class ModelA(models.Model):
name = models.CharField(max_length=30)
def save(self, force_insert=False, force_update=False):
is_new = self.id is None
super(ModelA, self).save(force_insert, force_update)
if is_new:
ModelB.objects.create(thing=self)
- [Django]-How do I get user IP address in Django?
- [Django]-Django query get last n records
- [Django]-How does Django Know the Order to Render Form Fields?
9👍
I know it’s a bit late, but I came up with a cleaner and more elegant solution.
Consider this code:
class ModelA(models.Model):
name = models.CharField(max_length=30)
@classmethod
def get_new(cls):
return cls.objects.create().id
class ModelB(models.Model):
thing = models.OneToOneField(ModelA, primary_key=True, default=ModelA.get_new)
num_widgets = IntegerField(default=0)
Of course any callable (except lambdas) can be used to supply the default, as long as you return integer id of related object 🙂
- [Django]-Django ModelForm with extra fields that are not in the model
- [Django]-How to combine multiple QuerySets in Django?
- [Django]-Numeric for loop in Django templates
8👍
I assembled a few different answers (because none of them worked straight out of the box for me) and came up with this. Thought it’s pretty clean so I’m sharing it.
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=ModelA)
def create_modelb(sender, instance, created, **kwargs):
if created:
if not hasattr(instance, 'modelb'):
ModelB.objects.create(thing=instance)
It’s using Signal as @Dmitry suggested. And as @daniel-roseman commented in @jarret-hardie’s answer, Django Admin does try to create the related object for you sometimes (if you change the default value in the inline form), which I ran into, thus the hasattr check. The nice decorator tip is from @shadfc’s answer in Create OneToOne instance on model creation
- [Django]-Django BooleanField as radio buttons?
- [Django]-Django 1.7 – App 'your_app_name' does not have migrations
- [Django]-How do I return JSON without using a template in Django?
0👍
You could use the post_save-hook which is triggered after a record has been saved. For more documentation on django signals, see here. On this page, you find an example on how to apply the hook on your model.
- [Django]-Error trying to install Postgres for python (psycopg2)
- [Django]-Django Reverse Accessor Clashes
- [Django]-Select DISTINCT individual columns in django?
0👍
I think you want to use django’s model inheritance. This is useful if the following statement is true: ModelA is a ModelB (like, Restaurant is a Location).
You can define:
class ModelB(models.Model):
field1 = models.CharField(...)
class ModelA(ModelB):
field2 = models.CharField(...)
Now you can create an instance of ModelA and set field2 and field1. If this model is saved it will also create an instance of ModelB which gets the value of field1 assigned. This is all done transparently behind the scenes.
Though you can do the following:
a1 = ModelA()
a1.field1 = "foo"
a1.field2 = "bar"
a1.save()
a2 = ModelA.objects.get(id=a1.id)
a2.field1 == "foo" # is True
a2.field2 == "bar" # is True
b1 = ModelB.objects.get(id=a1.id)
b1.field1 == "foo" # is True
# b1.field2 is not defined
- [Django]-Adding new custom permissions in Django
- [Django]-How to add an model instance to a django queryset?
- [Django]-Error: No module named staticfiles
0👍
Just create a function that creates and returns an empty ModelA, and set the default named argument on “thing” to that function.
- [Django]-Django post_save() signal implementation
- [Django]-Choose test database?
- [Django]-How to monkey patch Django?
0👍
If you are using InlineForm in admin panel, than you can do like this.
But of course in other cases need to check too(like in DRF or manual model instance creation)
from django.contrib import admin
from django.forms.models import BaseInlineFormSet, ModelForm
class AddIfAddParentModelForm(ModelForm):
def has_changed(self):
has_changed = super().has_changed()
if not self.instance.id:
has_changed = True
return has_changed
class CheckerInline(admin.StackedInline):
""" Base class for checker inlines """
extra = 0
form = AddIfAddParentModelForm
- [Django]-Django connection to PostgreSQL: "Peer authentication failed"
- [Django]-Can one use the Django database layer outside of Django?
- [Django]-What's the best way to handle Django's objects.get?