I have a dataset where users may need to edit old records but also choices need to be limited to a set of "active" values. This means the legacy choices need to be added to the queryset of the ModelChoiceField when an instance containing a non-active value is selected.
In my mind the ideal way to do this would be to subclass ModelChoiceField however I cannot seem to find the insertion point of instance data into ModelChoiceField. I am able to make it work by setting the queryset in forms.py but I have a large number of fields this is needed for and was really hoping for a more pythonic/DRY solution.
For example:
models.py
class ActiveChoiceModel(models.Model):
name = models.CharField(max_length=10)
active = models.NullBooleanField(default=False)
class MyModel(models.Model):
fk_activechoicemodel = models.ForeignKey(to='mydb.ActiveChoiceModel')
The queryset for the ModelChoiceField should be:
ActiveChoiceModel.objects.filter(active=True) | ActiveChoiceModel.objects.filter(pk=instance.fk_activechoicemodel.id)
This can be achieved in forms.py as:
Class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
if self.instance.fk_activechoicemodel:
self.fields['fk_activechoicemodel'].queryset = ActiveChoiceModel.objects.filter(active=True) | ActiveChoiceModel.objects.filter(pk=instance.fk_activechoicemodel.id)
Any thoughts on how to do this clean and DRY for 10's or 100's of 'ActiveChoiceModels'?
I think the best solution is a custom models.Manager to get the special queryset, but the real work is done with a Q expression, so if you don't have to pull this queryset anywhere else, it might be possible to only use the Q expression. Not having your code I haven't tested this, but its modified from a custom manager that I'm running, plus this answer.
# models.py
from django.db import models
from django.db.models import Q
class AvailableChoiceManager(models.Manager):
"""Active choices + existing choice even if its now inactive.
"""
def get_queryset(self, pk=None):
qs = super().get_queryset() # get every possible choice
current_choice = ChoiceModel.objects.get(pk=pk):
return qs.filter((Q(pk=pk) | Q(active=True))
In your model definition, you need to add an instance of this manager:
#models.ActiveChoiceModel
AvailableChoices=AvailableChoiceManager()
Invoke with:
qs = AvailableChoiceModel.AvailableChoices(pk=pk)
get a list of the active choices:
choice_list = list(Choices.objects.filter(active=True).values_list('id', 'slug'))
if fk.pk not in choice_list:
choice_list.insert(0, (fk.pk, fk.slug))
set the choices attribute of the widget to choices_list:
self.fields['fk'].widget = forms.Select(
choices=choice_list,
attrs={'pk': 'lug', 'size': 7, 'style': "width:100%"})
I am not sure there is a way to do this with less than three or four lines of code, but I don't see that as a lot.
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.