简体   繁体   中英

Filter Django model on reverse relationship list

I have two Django models as follows:

class Event(models.Model):
    name = models.CharField()

class EventPerson(models.Model):
    event = models.ForeignKey('Event',on_delete='CASCADE',related_name='event_persons')
    person_name = models.CharField()

If an Event exists in the database, it will have exactly two EventPerson objects that are related to it.

What I want to do is to determine if there exists an Event with a given name AND that have a given set of two people (EventPersons) in that event. Is this possible to do in a single Django query?

I know I could write python code like this to check, but I'm hoping for something more efficient:

def event_exists(eventname,person1name,person2name):
    foundit=False
    for evt in Event.objects.filter(name=eventname):
        evtperson_names = [obj.person_name in evt.event_persons.all()]
        if len(evtperson_names) == 2 and person1name in evtperson_names and person2name in evtperson_names:
            foundit=True
            break
    return foundit

Or would it be better to refactor the models so that Event has person1name and person2name as its own fields like this:

class Event(models.Model):
    name = models.CharField()
    person1name = models.CharField()
    person2name = models.CharField()

The problem with this is that there is no natural ordering for person1 and person2, ie if the persons are "Bob" and "Sally" then we could have person1name="Bob" and person2name="Sally" or we could have person1name="Sally" and person2name="Bob".

Suggestions?

You can query for EventPerson objects where the event name is as given instead, use the values_list to extract the person_name field, and convert the returning list of values to a set for an unordered comparison:

def event_exists(eventname, person1name, person2name):
    return set(EventPerson.objects.filter(event__name=eventname).values_list(
        'person_name', flat=True)) == {person1name, person2name}

I modified @blhsing answer slightly adding a filter on names.

def event_exists(eventname, person1name, person2name):
    event_people = EventPerson.objects.select_related('event').filter(person_name__in=[person1name, person2name], event__name=eventname)
    return set(event_people.values_list('person_name', flat=True)) person1name, person2name}

I would suggest passing EventPerson objects or theird ids to this function instead of just names, would make filtering easier (you wouldn't need a set and filter straight by ids) and more efficient (by using db indices... or you would have to index person_name as well)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM