saving inherited model overwrites base model

48 views
Skip to first unread message

Kalev Takkis

unread,
Jul 5, 2019, 1:42:33 PM7/5/19
to Django users
Hi all,

tl;dr: saving inherited model overwrites fields in parent models with empty strings

I have the following models:

class Contact(models.Model):
    comment = models.CharField(max_length=255, blank=True)
    emails = models.ManyToManyField(
        Email,
        blank=True,
        through='ContactEmail',
        through_fields=('contact', 'email'),
    )

    # couple of more manytomanys, to addresses, phones, etc

       
class ContactEmail(BaseContactJunctionModel):
    email = models.ForeignKey(Email, null=True, on_delete=models.SET_NULL)
    contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
    event = models.ForeignKey(ContactEvent, on_delete=models.PROTECT)
    event_date = models.DateTimeField(default=timezone.now)


# other similar through tables for the rest of the manytomanys and their target models


class Organisation(Contact):

    name = models.CharField(max_length=255, blank=True)
    hierarchy = models.ManyToManyField(
        'self',
        blank=True,
        symmetrical=False,
        through='OrganisationHierarchy',
        through_fields=('sender', 'target'),
    )   

    # again, more manytomanys, not to self though

# not sure this is relevant but as it's to self, then just in case
class OrganisationHierarchy(BaseContactJunctionModel):
    # this, instance that specifies relationship
    sender = models.ForeignKey(
        Organisation,
        null=True,
        related_name='senders',
        on_delete=models.SET_NULL,
    )
   
    # instance that 'this' or 'sender' is related to
    target = models.ForeignKey(
        Organisation,
        null=True,
        related_name='targets',
        on_delete=models.SET_NULL,
    )

    connection_type = models.ForeignKey(
        ConnectionType,
        null=True,
        on_delete=models.PROTECT,
    )

I have a view based on CreateView and a template to capture user
input. The post request coming from browser is a-ok, saving
emails, contact, contactemails and other manytomany targets and
junction tables works without a problem but when getting to
saving Organisation, this happens (pseudocode):

save() is called from django/db/models/base.py
    save_base() is called with self=Organisation
        save_base calls _save_parents(self=Organisation, cls=Organisation)
            save_parents loops through cls._meta.parents and comes across relation
                parent=Contact, field=Organisation.contact_ptr
                calls _save_table with self=Organisation and cls=Contact
                    _save_table finds from cls._meta that it has
                    local concrete field "comment" and stores it in list non_pks
                    it then finds the values for those fields like this:                
                    f.pre_save(self, false) for f in non_pks


I then see a call to _update in queries.py and Contact instance
is updated with new values.

I don't really understand what's happening here, the code finds
local concrete fields from "cls" which at this point is Contact
and comes up, correctly, with "comment" but then it tries to
query the field value form "self" which at this point is
Organisation object. I'm not sure why Organisation has "comment"
attribute at all, because the way I read the docs it should only
happen when the base model is abstract or when the inherited
model is proxy model, neither of which is the case. Anyway, the
value returned by save_base for the "comment" from "self" is not
the value I inserted into the form field (and what has previously
been saved to Contact object) but is instead an empty string.

The end result is, after Contact object is correctly saved it is,
moments later, overwritten by an empty string. Does anybody have
a clue as to what is happening and most importantly, how can I
avoid it?

Thanks,

Jani Tiainen

unread,
Jul 5, 2019, 4:29:32 PM7/5/19
to django...@googlegroups.com
Hi.

I think you have a bit confused terms model inheritance and abstract models.

Normal model inheritance in Django means that there will be implicit one to one relation to base model. Inherited model still receives all fields from parent model. On save data is just saved to correct models. To both parent and child. And both models do have separate tables.

If inherited model is abstract all fields are copied to inherited model and django treats model as one. There is no parent table created but columns are added to inheriting model table.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/4587198f-5f6f-49a7-b54a-1dcfe5a436ab%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kalev Takkis

unread,
Jul 11, 2019, 11:52:03 AM7/11/19
to Django users
If it receives fields from parent model, I would assume it actually has access to them. In my case, 'comment' field queried from child model returns empty while the field in parent has value.
Something's definitely wrong here, I removed inheritance and created explicit OneToOne to Contact and no more monkey-business, fields are saved as expected.


On Friday, July 5, 2019 at 9:29:32 PM UTC+1, Jani Tiainen wrote:
Hi.

I think you have a bit confused terms model inheritance and abstract models.

Normal model inheritance in Django means that there will be implicit one to one relation to base model. Inherited model still receives all fields from parent model. On save data is just saved to correct models. To both parent and child. And both models do have separate tables.

If inherited model is abstract all fields are copied to inherited model and django treats model as one. There is no parent table created but columns are added to inheriting model table.

To unsubscribe from this group and stop receiving emails from it, send an email to django...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages