Can a generic.GenericForeignKey() field be Null?
Asked Answered
D

3

34

I am creating an object which tracks changes (Updates) regarding the creation, updating and deletion of other so called UUIDSyncable objects in the database.

This involves any object which extends the UUIDSyncable classes's save() and delete() methods being overriden, in such a way that it creates a new Update object recording the action (either insert, update or delete) and originally a models.CharField(max_length=64) field specifying the UUID pk of of the of UUIDSyncable objects.

At this point I wanted to update the implementation to use Django's generic.GenericForeignKey() field in stead of my single character field. This would enable be to access the object about which the update is being recorded in a much simpler way.

My problem arrises when you wish to delete an object and store that as an Update object, as you know Django preforms a cascade delete on the UUIDSyncable object being deleted, deleting all the previous Update records (which is not wanted).

When the UUIDSyncable object has been deleted in my original implementation, each Update object would still retain the UUID to inform external parties about its deletion. My first solution to emulate this was to set content_object = None just before deleting the object on all the Update objects, thus preventing the deletion cascade.

It seams that generic.GenericForeignKey() cannot be set to allow NULL values. Is there a way to do this, or if not how can a standard CharField holding a UUID primary key and a sperate CharField holding the model name be used in a similar fashion?

Delitescent answered 1/3, 2010 at 16:9 Comment(0)
B
35

Perhaps you need to set null=True on the underlying content_type and object_id fields that make up the generic foreign key.

Belted answered 1/3, 2010 at 19:41 Comment(1)
I have tried allowing content_type to be null but not object_id, as I always wish to keep the value the object id (charField PK is this case) has for reference later on (even after the object it specifies) has been deleted. This I remember did not seam to allow me to ascertain my goal.Delitescent
R
56

For the lazy/pragmatic:

class MyModel(models.Model):
    ...other fields...
    content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=models.SET_NULL)
    object_id = models.PositiveIntegerField(blank=True, null=True)
    content_object = generic.GenericForeignKey('content_type', 'object_id')
Ritchey answered 12/1, 2015 at 23:3 Comment(7)
I love it when I Google and stumble across my own answer...d'ohRitchey
content_type & object_id are de default names, you dont even need to set them as arguments content_object = generic.GenericForeignKey() would be enoughDelft
@Delft you have to set them to explicitly have blank=True / null=True on those fields so that the content_object can be null, which is what the question is about.Ritchey
@MarkChackerian yes i konw ;-) thats why i said that you dont need to set content_type & object_id as arguments because they are default. You define content_type & object_id and then you simply do content_object = generic.GenericForeignKey()Delft
It is also advisable to set on_delete=models.SET_NULL constraint to content_type since default is CASCADE.Sera
@AntoinePinsard thanks -- I have updated my answer, as on_delete is now required for Django 2.0Ritchey
@Delft -- so annoying when people rely on the default field names instead of making it explicit. I'm happy your advice was ignored.Rudiger
B
35

Perhaps you need to set null=True on the underlying content_type and object_id fields that make up the generic foreign key.

Belted answered 1/3, 2010 at 19:41 Comment(1)
I have tried allowing content_type to be null but not object_id, as I always wish to keep the value the object id (charField PK is this case) has for reference later on (even after the object it specifies) has been deleted. This I remember did not seam to allow me to ascertain my goal.Delitescent
W
-3

Have you tried setting null=True on the GenericForeignKey? Generic foreign keys aren't a concept that is recognised by the low-level database application (i.e. MySQL, SQLite, etc.) so you should be able to specify that the field be null.

null=True on a database field in Django signifies that the database field representing that data is allowed to be empty, whereas blank=True tells the Django forms/model validation that the field is OK to be left empty by the user (or you, through the admin interface). Therefore, you might need to use both to achieve what you're after.

Weekend answered 1/3, 2010 at 18:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.