Many-to-Many Multiplechoice Form with More Information

In the previous version of my application, I had many, many relationships between the account and the club. In my AccountForm, I used "club = forms.MultipleChoiceField (widget = CheckboxSelectMultiple)" so that the user can select from a complete list of clubs.

β–’ Football

β–’ Hockey

β–’ Tennis

β–’ Swimming

However, now I need to include an optional field where they can include their membership reference number, if any. So something like

β–’ Football ________

β–’ Hockey ________

β–’ Tennis ________

β–’ Swimming ________

I understand that I need to use the through model, but now I am trying to replicate the layout of the multiple choice style that I had before.

a) I assume that I need to use the built-in set of forms, but based on a through table, so I somehow need to get a formet factory to create forms for each of the clubs. I do not know how to do that. The evidence?

b) Check the box to reflect membership in this club. Presumably, this is a logical field with a hidden field denoting the identifier of the club, and then some user work, clean and preserving functions.

Is this correct, or is there an easier way?

 class Account(models.Model): name = models.CharField(max_length=20) address_street01 = models.CharField(max_length=50) address_pc = models.CharField(max_length=10) address_city = models.CharField(max_length=50) class Club(models.Model): name = models.CharField(max_length=30, unique=True) class Membership(models.Model): club = models.ForeignKey(Club) account = models.ForeignKey(Account) membership_ref = models.CharField(max_length=50, blank=True) 
+6
source share
1 answer

We use ModelFormSetView from django-extra-views for a similar use case. This is not supported by the through model, but a table with many relationships, in which many relationships with all their attributes are displayed as part of a detailed representation of the underlying model associated with ForeignKey.

It will work for the through model, simply by providing the through Model as an attribute of the model ModelFormSetView . When saving or even earlier, through get_extra_form_kwargs you will need to set a link to an instance of the main model, which defines the m2m field.

The difficult thing with regular django FormSets (for me) is that it is mainly designed to create new objects, while we only need to display existing objects and modify them. Basically, we need repeating forms filled with raw data, which are saved immediately. You can also delete them.

View

 # You could additionally try to inherit from SingleObjectMixin # if you override the methods that refer to cls.model class ImportMatchView(ImportSessionMixin, ModelFormSetView): template_name = 'import_match.html' model = Entry # this is your through model class form_class = EntryForm can_delete = True def get_success_url(self): return self.get_main_object().get_absolute_url() def get_factory_kwargs(self): kwargs = super().get_factory_kwargs() num = len(self.get_match_result()) kwargs['extra'] = num # this controls how many forms are generated kwargs['max_num'] = num # no empty forms! return kwargs def get_initial(self): # override this if you have to previous m2m relations for # this main object # this is a dictionary with the attributes required to prefill # new instances of the through model return self.get_match_result() # this fetches data from the session def get_extra_form_kwargs(self): # you could add the instance of the m2m main model here and # handle it in your custom form.save method return {'user': self.request.user} def get_queryset(self): # return none() if you have implemented get_initial() return Entry.objects.none() # return existing m2m relations if they exist # main_id = self.get_object().pk # SingleObjectMixin or alike # return Entry.objects.filter(main=main_id) def formset_valid(self, formset): # just some example code of what you could do main = self.get_main_object() response = super().formset_valid(formset) main_attr_list = filter(None, [form.cleaned_data.get('entry_attr') for form in formset.forms]) main.main_attr = sum(main_attr_list) main.save() return response 

The form

Regular Django ModelForm for your through model. As here, provide a link to an instance of the model defining the m2m field so that you can assign it before saving.

 def __init__(self, *args, user=None, **kwargs): self.user = user super().__init__(*args, **kwargs) def save(self, commit=True): self.instance.owner = self.user return super().save(commit) 

Template

 <form id="the-matching" method="POST" action="{{ save_url }}" data-session-url="{{ session_url }}"> {% csrf_token %} {{ formset.management_form }} <ul class="match__matches"> {% for form in formset %} {% include 'import_match__match.html' %} {% endfor %} </ul> </form> 

In each form (inside import_match__match.html ), you import_match__match.html over the fields in the usual django way. Here is an example of hidden fields:

 {% for field in form %} {% if field.value %} <input type="hidden" name="{{ form.prefix }}-{{ field.name }}" value="{{ field.value }}"/> {% endif %} {% endfor %} 

Form processing for the main object:

  • you can create two views and send them to both via JS after clicking one "Save" button.
  • or you can submit one view (e.g. above) and create a form for the main object explicitly in get () and post (), and then save it when formet_valid is called.
  • You can also try to implement both ModelFormsetView and FormView and override all the appropriate methods to handle both instances of the forms (form instance and main form).
+3
source

Source: https://habr.com/ru/post/1013023/


All Articles