addohm
addohm

Reputation: 2475

Django inline formset with manytomany fields

Ive spent a fair bit of time searching on this subject without finding some real up to date answers. I'm trying to create a form that creates a db entry. The basic idea is this:

Many events can have many people

So, the struggle here is that the user needs to create an event where the user can select all the people that attend. Each person that attends though, has certain things that also needs to be tracked per event. See the model below:

models.py

from django.db import models
from django.contrib.auth.models import User[]


class PersonRole(models.Model):
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, unique=True)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        return self.role


class PersonClass(models.Model):
    name = models.CharField(max_length=100, choices=CLASS_CHOICES, unique=True)
    color = models.CharField(max_length=6, choices=COLOR_CHOICES, unique=True)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        return self.name


class Person(models.Model):
    name = models.CharField(max_length=100, unique=True)
    personclass = models.ForeignKey(PersonClass, on_delete=models.CASCADE, blank=True, null=True)
    personrole = models.ForeignKey(PersonRole, on_delete=models.CASCADE, blank=True, null=True)
    value = models.IntegerField(default=0)
    reliability = models.IntegerField(default=0)
    last_item = models.DateField(auto_now=False, blank=True, null=True)
    last_event_attended = models.DateField(auto_now=False, blank=True, null=True)
    last_manager_attended = models.DateField(auto_now=False, blank=True, null=True)
    item_received = models.BooleanField(default=False)
    note = models.TextField(null=True, blank=True)
    core_attendee = models.BooleanField(default=False)
    enabled = models.BooleanField(default=True)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        return self.name


class Location(models.Model):
    name = models.CharField(max_length=100, unique=True)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        return self.name


class Boss(models.Model):
    name = models.CharField(max_length=100, unique=True)
    location = models.ForeignKey(Location, on_delete=models.CASCADE)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        return self.name


class Raid(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    boss = models.ForeignKey(Boss, on_delete=models.CASCADE, null=True, blank=True)
    success = models.BooleanField()
    attendees = models.ManyToManyField(Person)
    created_by = models.ForeignKey(User,
        related_name="raids", blank=True, null=True,
        on_delete=models.SET_NULL)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        return str(self.date)

I've started down the path of just trying to use the generic in-built create\update\delete views and ran into this:

ValueError: 'roster.Person' has no ForeignKey to 'roster.Raid'.

forms.py

class RaidGenericCreateModelForm(forms.ModelForm):
    class Meta:
        model = Person
        exclude = ()


RaidPersonFormSet = inlineformset_factory(Raid, Person, fields=['name', 'personclass', 'personrole', 'item_received'], extra=1, can_delete=False)

views.py

class RaidCreate(CreateView):
    model = Raid
    template_name = 'roster/raid_create.html'
    form_class = RaidGenericCreateModelForm
    success_url = None

    def get(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        person_form = RaidPersonFormSet
        return self.render_to_response(
            self.get_context_data(form=form,
                                  person_form=person_form
                                  )
        )

There are 9-year old posts that say you cannot use inlineformset_factory with many to many fields. So my question here is, what are my options? What is the best way to go about simply creating an Event (referred to as Raid in the model) and at the same time selecting the people from the roster (referred to as Person in the model) and changing the options those people have associated to them for that event?

As an example of what I am trying to accomplish here: Event 1

-Person A (selected, item_received=True)

-Person B (selected, item_received=False)

-Person C (selected, item_received=False)

-Person D (not selected, item_received=False)

Event 2

-Person A (selected, item_received=False)

-Person B (not selected, item_received=False)

-Person C (selected, item_received=True)

-Person D (selected, item_received=False)

Where the list of persons is showing all persons and some of the persons fields from the Person model.

Upvotes: 3

Views: 299

Answers (1)

Gilbish Kosma
Gilbish Kosma

Reputation: 897

The alternate thing you can do is use DjangoRestFramework for this purpose. Using rest you can first send persons data to frontend then in frontend you can create Event and add person details for each event,and in last post all that data using javascript.Try it,it will surely work.

Upvotes: 1

Related Questions