I am struggling with getting a usable many to many formset working – I have it largely functional, but from a UI perspective it is unusable, because it shows a select list with 16,000 objects
in it (ie DirectoryLink3 object (32)
is in the list, rather than Take away shops
).
You can imagine that getting/formatting a select with 16,000 objects, multiple times, is a little slow, not even considering the time it takes to render in a browser.
As it is the forms save/delete formsets as expected. However, in this case I do not want a select list for each form, I just want the description
field from the DirectoryLink3
model as a label/text (not input) and a delete checkbox for each form in the formset.
That is to say that the user won’t be able to edit the description
, just delete the row. To add a row I have a separate autocomplete input categories
, this uses javasccript to populate the required hidden form elements for a new row. The categories
autocomplete seems to be working as expected, although I have yet to test it actually adding a record.
So, in breif: how to get the description
field data without 16,000 siblings, show it as text on the form and control what ever other elements are in the object (ie do not show them).
models.py
…
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
…
directory_optin = models.BooleanField(_('Directory opt-in'), default=False, db_index=True)
directory_desc = models.CharField(_('Business description'), max_length=512, default='', blank=True, null=False, db_index=True)
directory_classes = models.ManyToManyField('DirectoryLink3', through='directorylink_profiles' )
class DirectoryLink3(models.Model):
id = models.BigAutoField(primary_key=True)
profiles = models.ManyToManyField('Profile', through='DirectoryLink_profiles')
uplink = models.CharField(max_length=16, blank=True, null=True, db_index=True)
catid = models.CharField(max_length=16, blank=True, null=True, db_index=True)
description = models.CharField(max_length=128, blank=True, null=True, db_index=True)
createdate = models.DateTimeField(auto_now_add=True)
moddate = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'directorylink_3'
class DirectoryLink_profiles(models.Model):
id = models.BigAutoField(primary_key=True)
directorylink = models.ForeignKey(DirectoryLink3, null=True, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, verbose_name=_('profile'), null=True, on_delete=models.CASCADE)
views.py
class DirectoryUpdateView(UpdateView):
template_name = 'directory/edit_directory.html'
form_class = DirectoryForm
model = Profile
success_url = reverse_lazy('directory:directory-edit')
#tried this, did not seem to work
#formset_class = inlineformset_factory(Profile, DirectoryLink_profiles, fields=('directorylink', 'profile'))
#required because there is no pk or slug
def get_object(self, queryset=None):
return Profile.get_or_create_for_user(self.request.user)
def form_valid(self, form):
print('form: ', dir(form))
if form.data['directory_desc'] == '':
messages.warning(self.request, 'The ‘directory description’ field is empty, \
this may not be an error, but it is more useful to complete this field.')
#form.add_error('directory_desc', 'Hello')
self.object = form.save()
messages.success(self.request, 'Changes to item have been saved')
context = self.get_context_data(form=form)
formset = context['directorychoice']
if formset.is_valid():
response = super().form_valid(form)
formset.instance = self.object
formset.save()
return response
else:
return super().form_invalid(form)
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
#print('self.formset: ', self.formset)
log_views(self)
return super(DirectoryUpdateView, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
context = super(DirectoryUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['directorychoice'] = DirectoryChoice(self.request.POST, instance=self.object)
context['directorychoice'].full_clean()
else:
context['directorychoice'] = DirectoryChoice(instance=self.object)
return context
forms.py
class DirectoryForm(forms.ModelForm):
directory_desc = forms.CharField(label=_('Directory description'), widget=forms.Textarea(attrs={'rows':5, 'blank': True}), required=False, help_text=_('Give a short description of your business, up to 512 characters') )
directory_list = forms.CharField(label=_('Categories'), help_text=_('Type a few characters and select from the pop-up menu'), required=False)
class Meta:
model = Profile
fields = (
'directory_optin',
'directory_desc',
'directory_list',
)
DirectoryChoice = forms.models.inlineformset_factory(
Profile,
DirectoryLink_profiles,
fields = ['id', 'directorylink', 'profile'],
exclude = [],
can_delete = True,
max_num=3,
extra=1,
)
edit_directory.html
…
{{ directorychoice.management_form }}
…
{% bootstrap_formset directorychoice layout='horizontal' %}
…
Sorry, that posted before I’d finished.