alias51
alias51

Reputation: 8648

How to query model instances outside of the current object

I have the following models:

class PermanentList(models.Model):
      permanent_employees = models.ManyToManyField(Employee, through='PermanentMembership')

class EventList(models.Model):
      event_employees = models.ManyToManyField(Employee, through='EventMembership')

class Employees(models.Model):
      email = models.EmailField()

And the following through models to handle the M2M relationship:

class PermanentMembership(models.Model):
      permanent_list = models.ForeignKey(PermanentList, null=True, on_delete=models.PROTECT)
      permanent_employees = models.ForeignKey(Employee,null=True, on_delete=models.PROTECT)

      class Meta:
           constraints = [models.UniqueConstraint(
                               fields=['permanent_list', 'permanent_employees'],
                               name="unique_event_list_employees")]

class EventMembership(models.Model):
      event_list = models.ForeignKey(EventList, null=True, on_delete=models.PROTECT)
      event_employees = models.ForeignKey(Employee,null=True, on_delete=models.PROTECT)

      def clean(self):
           // missing code

      class Meta:
           constraints = [models.UniqueConstraint(
                              fields=['event_list', 'event_employees'],
                              name="unique_event_list_employees")]

EDITS:

  • Each Employee can only be in 1 EventList or 1 PermanentList
  • EventList and PermanentList are completely separate models, only sharing Employees. They can't be combined and managed as one list.

2 Questions:

  1. How can I validate each event_employee using def clean(self): to ensure that if an Employee is already on a PermanentList they can not be added to a EventList?
  2. If an Employee is already on an EventList how can I create a function to transfer them to a PermanentList? Can this be done on a Model class or would it have to be in a View? How would I go about this?

Upvotes: 0

Views: 101

Answers (2)

Lord Elrond
Lord Elrond

Reputation: 16072

IIUC:

class EventMembership(models.Model):
    ...

    def clean(self):
        if PermanentMembership.objects.filter(
                permanent_employees__id=self.event_employees.id
            ).exists():
            raise ValidationError("Employee in PermanentList")

As for the second question, you could create an insert signal on PermanentMembership that checks if the employee is in EventMembership, and if it is, then delete it:

from django.db.models.signals import post_save
from django.dispatch import receiver

class PermanentMembership(models.Model):
    ...

@reciever(post_save, sender=PermanentMembership)
def handler(sender, **kwargs):
    try:
        event_employee = EventMembership.objects.get(id=sender.id)
        event_employee.delete()
    except EventMembership.DoesNotExist:
        pass

Unfortunately, this will hinder performance a bit, since you will have to query the PermanentMembership each time you insert on EventMembership. To circumvent this I would cache a list of employee ids that are in PermanentMembership, and update that list after each insert.

That being said, I would encourage you to reconsider your database design, because something seems out of place (with the limited info I have on the situation, at least). I could be completely wrong though, it really depends on the details.

Upvotes: 1

Iain Shelvington
Iain Shelvington

Reputation: 32314

Take this potential model layout for example, each Employee can only be assigned to 1 Job and can only be either PERMANENT or EVENT. What's wrong with having this layout?

from django.db import models

PERMANENT = 'PERMANENT'
EVENT = 'EVENT'


class Job(models.Model):
    name = models.CharField(max_length=100)

    @property
    def permanent_list(self):
        return self.employees.filter(type=PERMANENT)

    @property
    def event_list(self):
        return self.employees.filter(type=EVENT)


class Employee(models.Model):

    EMPLOYMENT_TYPES = (
        (PERMANENT, 'Permanent'),
        (EVENT, 'Event'),
    )

    email = models.EmailField()
    job = models.ForeignKey(Job, on_delete=models.PROTECT, related_name='employees')
    type = models.CharField(max_length=100, choices=EMPLOYMENT_TYPES)

Upvotes: 0

Related Questions