[Django]-Access model instance inside model field

4👍

First we can define a strategy for the Owner field that will call the function with the object that has been updated. We can define such deletion, for example in the <i.app_name/deletion.py file:

# app_name/deletion.py

def SET_WITH(value):
    if callable(value):
        def set_with_delete(collector, field, sub_objs, using):
            for obj in sub_objs:
                collector.add_field_update(field, value(obj), [obj])
    else:
        def set_with_delete(collector, field, sub_objs, using):
            collector.add_field_update(field, value, sub_objs)
    set_with_delete.deconstruct = lambda: ('app_name.SET_WITH', (value,), {})
    return set_with_delete

You should pass a callable to SET, not call the function, so you implement this as:

from django.conf import settings
from django.db.models import Q
from app_name.deletion import SET_WITH

def get_new_owner(event):
    invited_users = event.invites.order_by(
        'eventinvites__id'
    ).filter(~Q(pk=event.owner_id), is_active=True).first()
    if invited_users is not None:
        return invited_users
    else:
        event.delete()


class Event(models.Model):
    # …
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name='owned_events',
        verbose_name=_('Owner'),
        on_delete=models.SET_WITH(get_new_owner)
    )

Here we thus will look at the invites to find a user to transfer the object to. Perhaps you need to exclude the current .owner of the event in your get_new_owner from the collection of .inivites.

We can, as @AbdulAzizBarkat says, better work with a CASCADE than explicitly delete the Event object , since that will avoid infinite recursion where an User delete triggers an Event delete that might trigger a User delete: at the moment this is not possible, but later if extra logic is implemented one might end up in such case. In that case we can work with:

from django.db.models import CASCADE

def SET_WITH(value):
    if callable(value):
        def set_with_delete(collector, field, sub_objs, using):
            for obj in sub_objs:
                val = value(obj)
                if val is None:
                    CASCADE(collector, field, [obj], using)
                else:
                    collector.add_field_update(field, val, [obj])
    else:
        def set_with_delete(collector, field, sub_objs, using):
            collector.add_field_update(field, value, sub_objs)
    set_with_delete.deconstruct = lambda: ('app_name.SET_WITH', (value,), {})
    return set_with_delete

and rewrite the get_new_owner to:

def get_new_owner(event):
    invited_users = event.invites.order_by(
        'eventinvites__id'
    ).filter(~Q(pk=event.owner_id), is_active=True).first()
    if invited_users is not None:
        return invited_users
    else:  # strictly speaking not necessary, but explicit over implicit
        return None

Leave a comment