Django - Limit select field to queryset of foreign table? - django

I have two models as follows:
System_Contact
first_name
last_name
isOwner = CharField ('Y'/'N')
isMainContact = CharField ('Y'/'N')
System
mainContact = ForeignKey(System_Contact)
owner = ForeignKey(System_Contact)
billTo = ForeignKey(System_Contact)
So, when I show the System form in a web page, the user can select the mainContact owner and billTo contacts from a drop down menu to save to the System model. However, I want to filter the select fields in the System form so that they are like this:
mainContact Select box: -- only show System_Contacts that have isMainContact = 'Y'
owner Select Box: -- only show Syste_Contacts that have isOwner = 'Y'
As it is now, I know how to limit a select box by filtering the queryset, but I don't know how to filter the related Foreign Key querySet. Since the mainContact and owner fields are Foreign Keys, I need to filter the Foreign Table (System_Contact), not the table on which the form is built (System)
I know how to filter a normal, non Foreign Key type select box as follows:
form.fields["some_field"].queryset = Some_Model.objects.filter(some_field="Foo")
How would I 'extend' this so that it filters the Foreign table?
This is what I am trying currently, without success:
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact = 'Y')
Thanks

This is what I am trying currently, without success:
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact = 'Y')
Can you include your model form and view? That looks OK to me.
Another approach is to override the __init__ method of your model form and set the queryset there.
class SystemForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SystemForm, self).__init__(*args, **kwargs)
self.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact = 'Y')
class Meta:
model = System
As an aside, I would recommend using a BooleanField instead of a CharField with 'Y' and 'N' as choices.

That syntax looks correct. Are you receiving an error or is it just not filtering and showing everybody? Try the System_Contact.objects.get(id=<some valid id>) to see if it gets only one or more. If it gets more, perhaps it is being populated from a different call than the one intended.

Well this is embarrassing...
As I was pasting in my view and model form as per Alasdair's request, I noticed my error. Here is my (incorrect) view:
def system_contacts(request, systemID):
sys = System.objects.get(pk=systemID)
if request.method == 'POST':
form = System_Contacts_Form(request.POST, instance=sys)
form.fields["systemOwner"].queryset = System_Contact.objects.filter(systemOwner__exact='Y')
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact__exact = 'Y')
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
conts = Contact_List.objects.filter(systemID = sys.pk)
form = System_Contacts_Form(instance=sys)
return render_to_response('pages/systems/system_pages/contacts.html', {'sys':sys, 'form':form, 'conts':conts}, context_instance=RequestContext(request))
I had put the form.fields["systemOwner"]... part in the POST section of the view, not the GET section of the view.
Here is my corrected view:
def system_contacts(request, systemID):
sys = System.objects.get(pk=systemID)
if request.method == 'POST':
form = System_Contacts_Form(request.POST, instance=sys)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
conts = Contact_List.objects.filter(systemID = sys.pk)
form = System_Contacts_Form(instance=sys)
form.fields["systemOwner"].queryset = System_Contact.objects.filter(systemOwner__exact='Y')
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact__exact = 'Y')
return render_to_response('pages/systems/system_pages/contacts.html', {'sys':sys, 'form':form, 'conts':conts}, context_instance=RequestContext(request))
Now my corrected view works and the filtering works on the select inputs on the form. I would not have thought to look at that without your help.
Cheers :-)

Related

How to link two forms (wagtail form and django form) with a foreign key?

I'm using a django form in conjunction with a wagtail form. The django form will record some fields that will be on any form of this type: name, email and the wagtail form will record extra data defined by the form page creator specific to that instance.
I've overloaded the serve method to capture both sets of data and I can process both forms, but I'm stuck when trying to add the logic to relate the form contents to each other so that when one submission set is deleted, the other set will be as well. I think what I need is a foreign key.
The following code fails at form_submission.event_submission = a.id where I'd like to take the id from the wagtail form submission and add that as a foreign key to the django form, so that when the wagtail form portion is deleted, the other is deleted as well, and so that I can have a usable link between the two form submissions.
def serve(self, request, *args, **kwargs):
if request.method == 'POST':
form = EventSignupForm(request.POST)
wagtail_form = self.get_form(request.POST, request.FILES, page=self, user=request.user)
if form.is_valid() and wagtail_form.is_valid():
a = self.process_form_submission(wagtail_form)
form_submission = form.save(commit=False)
form_submission.event_submission = a.id
form_submission.save()
return self.render_landing_page(request, form_submission, *args, **kwargs)
else:
form = EventSignupForm()
wagtail_form = self.get_form(page=self, user=request.user)
context = self.get_context(request)
context['form'] = form
context['wagtail_form'] = wagtail_form
return TemplateResponse(
request,
self.get_template(request),
context
)
The form submission class and django model form looks like this. I think the ForeignKey I have in the Model isn't right, but I don't know. Any help?
class EventFormSubmission(AbstractFormSubmission):
cancellation_id = models.CharField(max_length=7)
class EventSignup(models.Model):
"""
A model to contain signup info for an event: name, email.
"""
event_submission = models.ForeignKey(EventFormSubmission, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
email = models.EmailField()
I solved this by adding the extra fields I wanted (name, email) to the EventFormSubmission and then using a regular django form (not a ModelForm) to collect these pieces of information.

Filtering a model on a foreign key

Alright, I am pulling my hair out (and I don't have much) I have created a FormView that uses 2 models. One model simply displays some information from a table (not editable) the other Model is a form that a user selects two items from a drop down box. I need to filter the first Dropdown box. Below is the code I am using that is not working:
views.py
def assign_load(request):
form = DispatchForm(request.POST or None)
loads = Load.objects.all().filter(active=True, dispatched=False,
picked_up=False, delivered=False,
billed=False,
paid=False).order_by('start_pickup_date')
context_dict = {'dispatch' : form, 'load' : loads}
if form.is_valid():
save_it = form.save()
save_it.save()
new_dispatch = Dispatch.objects.get(id=save_it.id)
fix_load = Load.objects.get(id=new_dispatch.load_number_id)
fix_load.dispatched = True
fix_load.save()
return HttpResponseRedirect('/dispatch/dispatch/')
return render(request, 'dispatch/dispatch_form.html', context_dict)
forms.py
class DispatchForm(ModelForm):
class Meta:
model = Dispatch
fields = ['load_number', 'truck', 'start_mileage', 'end_mileage',
'pickup_date',
'pickup_time', 'delivery_date', 'delivery_time', 'driver_pay',
'fuel_cost', 'miles',
'status']
def get_queryset(self):
return self.model.objects.filter(load_number__dispatched=False)
I am trying to filter the model in forms.py I have tried using def get(), def get_queryset() and def get_context_data, none of them are returning a filtered queryset...I know I am missing something simple but I am running out of ideas any help would be great...if you need more information let me know that as well.
Thanks for all your help!

Django initial value for MultiChoice Field ignored for ModelForm

this is my first post here and I am very new to Django but I just can't seem to find a solution for this problem... I've searched stackoverflow and google but nothing seems to work for me...
I have a wine-app and want to be able to add and remove wines from the user's stock. In the list of wines the user can choose a wine to add and the ID of this wine is passed in the POST data. Since the data is getting lost after the first time the view is rendered I saved the ID in a cookie, which is working, but the problem is when I work with ModelForm de user has to select the foreign key for the user and for the wine, which is bad, so I tried to make it hidden and set the Fk_user and Fk_wine after the user choose the number of bottles to be added but before validation. Here's the problem after google everyone suggested I should use the "initial" and pass that to the form, but this is clearly not working because if I make the fields visible in the form I can see that it is not preselected...
viewy.py:
def addStockView(request):
wineId = request.POST.get('addStock')
if 'addStock' in request.POST:
wine = get_object_or_404(Wine, idwine=int(wineId))
userId = request.user.id
user = get_object_or_404(AuthUser, id=userId)
if request.method == 'POST':
#wineIdNew = request.COOKIES.get('wineIdToAdd')
#wineNew = get_object_or_404(Wine, idwine=wineIdNew)
form = StockForm(request.POST, initial={'fk_wine': wineNew.idwine, 'fk_auth_user': user.id})
if form.is_valid():
form.save()
return redirect('home')
else:
form = StockForm(initial={'fk_wine': wine.id,
'fk_auth_user': user.id})
response = render(request, 'addToStock.html', {'form': form})
response.set_cookie('wineIdToAdd', wineId)
return response
forms.py:
class StockForm(forms.ModelForm):
#fk_wine = ModelChoiceField(queryset=Wine.objects.all(),
# widget=HiddenInput())
#fk_auth_user = ModelChoiceField(queryset=AuthUser.objects.all(),
# widget=HiddenInput())
class Meta:
model = UserWineStock
fields = ['fk_auth_user', 'fk_wine', 'number']
can anyone help me with this..?
Yes, initial data is ignored when a form is bound to submitted data.
Instead of using initial here, you should exclude those two fields from the form and set them on the created object:
form = StockForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.fk_wine = wine
item.fk_auth_user = request.user
item.save()
return redirect('home')
(Also, please don't call your fields things like fk_auth_user. Just call it user.)

Datefield passing None to post data form a django form

I have a django form that has a datefield in it
class SearchForm(Form):
#otherifields
birth_date = forms.DateField(widget=DateInput(attrs={'classs':'datepicker form-control'}))
In my view i get the forms posted data
post_data = form.cleaned_data
and try to access the date with
date = post_data['birth_date']
but no matter if i set a date on my template or not the value is always None. I use this for the date widget throught all my Django project, and works fine in my ModelForms. But it won't say error of wrong date etc.
my django view
def search(request):
form = SearchForm(request.POST or None)
if request.method == 'POST'::
post_data = form.cleaned_data
customers = Customer.objects.filter(first_name__icontains=post_data['first_name'],
last_name__icontains=post_data['last_name'],
middle_name__icontains=post_data['middle_name'],
gender=post_data['gender'],
email__icontains=post_data['email'],
telephone__icontains=post_data['telephone'],
work_phone__icontains = post_data['work_phone'],
mobile__icontains=post_data['mobile'],
address__icontains = post_data['address'],
region__icontains = post_data['region'],
state__icontains = post_data['state'],
municipality__icontains = post_data['municipality'],
postal_code__icontains = post_data['postal_code'],
country__icontains = post_data['country'],
)
if post_data['birth_date']:
customers = customers.filter(birth_date=post_data['birth_date'])
#some other and returns
But if i print post_data['birth_date'] I always get None. What could be wrong? Am I missing something with forms?
Your code segment
if post_data['birth_date']:
customers.filter(birth_date=post_data['birth_date'])
is outside the scope of the search function. That could also be the reason it's value is None

Change boolean value in Django ModelForm

I have field in my Model which indicates whether a user wants to receive emails
receive_invites = models.BooleanField(default=True, help_text="Receive an invite email from friends")
I also have a view with an option:
[ x ] I do not want to receive emails...
By default receive_invites is True therefore the checkbox is ticked. However, I'd like the user to tick the checkbox in order to change receive_invites to False. I did the following in my ModelForm to achieve this. Does anybody have a more elegant way of doing this?
class UnsubscribeForm(forms.ModelForm):
class Meta:
model = Entrant
fields = ('receive_invites')
def __init__(self, *args, **kwargs):
if kwargs.has_key('instance'):
instance = kwargs['instance']
if instance.receive_invites:
instance.receive_invites = False
else:
instance.receive_invites = True
super(UnsubscribeForm, self).__init__(*args, **kwargs)
and in the view I have this:
if request.method == 'POST':
unsubscribe_form = UnsubscribeForm(request.POST, instance=me)
if unsubscribe_form.is_valid():
receive_invites = unsubscribe_form.cleaned_data['receive_invites']
if receive_invites:
user.receive_invites = False
else:
user.receive_invites = True
unsubscribe_form.save()
return redirect('index')
else:
unsubscribe_form = UnsubscribeForm(instance=me)
Adding on to #DrTyrsa, it's unreasonable to go through so much convolution just to follow a field naming convention. If you're attached to that field name, you can always add a property to the model that maps the data field to a value you care about:
dont_receive_invites = models.BooleanField(default=False, help_text="Don't receive an invite email from friends")
#property
def receive_invites(self):
return not self.dont_receive_invites
You can't alter the model? If you can, create dont_receive_invites field instead and save a lot of time.
Why not just remove the words "do not" from the view?
[ x ] I want to receive emails...
Otherwise, I'd recommend changing UnsubscribeForm from a ModelForm to a plain Form. Then you can invert booleans all you want without resorting to trickery. It's more work, but it'll work if you can't just change the label.