Database:
Document has many Sections, Sections has many Comments
On each document page, there is a comment form that lets you pick the section (using a ModelChoiceField). The problem is that the ModelChoiceField will contain ALL sections for all documents.
So to limit them I do this:
class CommentForm(ModelForm):
def __init__(self, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
if self.instance:
logger.debug(self.instance.document_id) # Prints "None"
self.fields['section'].queryset = Section.objects.filter(document=self.instance.document)
# ^^^ Throws DoesNotExist as self.instance.document is None
and my view is just:
form = CommentForm()
How do I pass CommentForm a document id?
Edit: Tried in my view:
d = Document.objects.get(id=id)
c = Comment(d)
form = CommentForm(c)
but document_id is still None in CommentForm
You can pass the document id when initialising the form:
class CommentForm(ModelForm):
def __init__(self, doc_id=None, *args, **kwargs):
if doc_id:
self.fields['section'].queryset = Section.objects.filter(document__id=doc_id)
and in the view
def my_view(request):
...
doc = Document.objects(...)
form = CommentForm(doc_id = doc.id)
EDIT
I edited the second line of the view, which I think deals with your comment? (make doc.id) a keyword arguement
Related
I've been reading lots of questions like this on stackoverflow but none seem to work. All I want to do is make a filtered form dropdown. I'm not sure how do go about doing it. I get the error that main is not defined... but I'm sure that's because it's not initialized or something? I'm very confused lol.
My form code looks like this:
class AssignForm(ModelForm):
class Meta():
model = Address
fields = ['overseer','publisher', 'status']
def __init__(self, *args, **kwargs,):
super(AssignForm, self).__init__(*args, **kwargs)
self.fields['publisher'].queryset = Publisher.objects.filter(service_group=main)
Here is my View:
def assignment(request, pk_a):
assign = Address.objects.get(id=pk_a)
num = request.user.overseer.publisher_set.first()
main = num.service_group.id
print(main)
I would like to use the variable: main inside my form dropdown so I can limit the dropdown relative to the overseer. How can this be accomplished? Thanks!
form = AssignForm(main, request.POST, instance=assign)
context = {'form':form,}
return render(request, 'sms/assign.html', context )
Change your form to
class AssignForm(ModelForm):
class Meta():
model = Address
fields = ['overseer','publisher', 'status']
def __init__(self, main, *args, **kwargs,):
super(AssignForm, self).__init__(*args, **kwargs)
self.fields['publisher'].queryset = Publisher.objects.filter(service_group=main)
and change your Form instantiation in views to
form = AssignForm(main, request.POST, instance=assign)
Can Anyone please explain me what this error means??
I have done this in my views.py:
class FormListView(FormMixin, ListView):
def get(self, request, *args, **kwargs):
# From ProcessFormMixin
form_class = self.get_form_class()
self.form = self.get_form(form_class)
# From BaseListView
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty and len(self.object_list) == 0:
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
% {'class_name': self.__class__.__name__})
context = self.get_context_data(object_list=self.object_list, form=self.form)
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
class CompanyListView(LoginRequiredMixin,FormListView):
model = Company
form_class = daterangeform
paginate_by = 10
def get_queryset(self):
return company.objects.filter(User=self.request.user)
def get_context_data(self, **kwargs):
context = super(companyListView, self).get_context_data(**kwargs)
context['selectdate_list'] = selectdate.objects.filter(User=self.request.user).latest()
return context
And I am getting this error:
ValueError: earliest() and latest() require either fields as positional arguments or 'get_latest_by' in the model's Meta.
Can anyone please explain me what is wrong in my code and possible solution for doing it in correct way...
Thank you
As specified in the documentation for latest(*fields) [Django-doc]:
Returns the latest object in the table based on the given
field(s).
This example returns the latest Entry in the table, according to the
pub_date field:
Entry.objects.latest('pub_date')
So if you want to obtain the latest object with respect to a field (for example updated_date), you can write this as:
selectdate.objects.filter(
User=self.request.user
).latest('updated_date')
You can however use the latest() without parameters, given you specified the order for this in the Meta class of the model, like:
class Foo(models.Model):
name = models.CharField(max_length=20)
class Meta:
get_latest_by = ['name']
In that case
Foo.objects.latest()
will give the Foo object with the maximum name (if we here compare the names lexicographically).
I have trouble setting up a form with a ModelMultipleChoiceField where the queryset depends on the user. My goal is to implement an export function.
My view looks like this:
class ExportView(FormView):
template_name = 'ExportTemplate.html'
def get(self, request, *args, **kwargs):
self.form_class = ExportForm(user = request.user)
return render(request, self.template_name, {'form': self.form_class})
def get_success_url(self):
return '/addrbook/'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super().form_valid(form)
form:
class ExportForm(forms.Form):
def __init__(self, user, *args, **kwargs):
usersContacts = ContactManager().getAllUsersContacts()
self.contactList = forms.ModelMultipleChoiceField(queryset = usersContacts[str(user)])
print(usersContacts[str(user)])
super(ExportForm, self).__init__(*args, **kwargs)
I verified that the queryset is not empty, it contains a list of model objects.
My template looks like this:
<form method="post">{% csrf_token %}
{{ form }}
<input type="submit">
</form>
the only thing that gets rendered is the submit button.
Another thing that left me completely unsure of python basics is that this code:
class ExportForm(forms.Form):
contactList = forms.ModelMultipleChoiceField(queryset = [])
def __init__(self, user, *args, **kwargs):
usersContacts = ContactManager().getAllUsersContacts()
self.contactList.queryset = usersContacts[str(user)]
print(usersContacts[str(user)])
super(ExportForm, self).__init__(*args, **kwargs)
returned the runtime error:
'ExportForm' object has no attribute 'contactList'
How is it possible? the contactList member is part of the ExportForm class definition and 'self' should point to an object of that class.
Could someone explain to me why the form field is rendered empty and/or point me to a better way to pass the user to the form?
Edit: here are the changes i made to the answer to get it working, although i now stumbled upon a different problem(the field expects a queryset, and not a list of model objects):
View:
class ExportView(FormView):
template_name = 'ExportTemplate.html'
form_class = ExportForm
def get_form_kwargs(self):
kwargs = super(ExportView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
form:
class ExportForm(forms.Form):
contactList = forms.ModelMultipleChoiceField(queryset = Contact.objects.none())
def __init__(self, *args, **kwargs):
user = kwargs['user']
kwargs.pop('user', None)
super(ExportForm, self).__init__(*args, **kwargs)
usersContacts = ContactManager().getAllUsersContacts()
self.fields['contactList'].queryset = usersContacts[str(user)]
print(self.fields['contactList'].queryset)
First of all, you should pass the user to the form every time you instantiate it, not just in the get method. The way to do this with FormView is to override get_form_kwargs.
def get_form_kwargs(self):
kwargs = super(ExportForm, self).get_form_kwargs()
kwargs[user] = self.request.user
return kwargs
You can then remove your get() method.
Then, in your form class, you should use the none() method instead of an empty list to get an empty queryset. In the __init__ method you can pop the user from kwargs, and then call super() before you edit the fields. You edit the contactList field via self.fields instead of self.contactList. Note that the recommended style for field names in Django is contact_list instead of contactList.
class ExportForm(forms.Form):
contactList = forms.ModelMultipleChoiceField(queryset=YourModel.objects.none())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(ExportForm, self).__init__(*args, **kwargs)
usersContacts = ContactManager().getAllUsersContacts()
self.fields['contactList'].queryset = usersContacts[str(user)]
You haven't shown the ContactManager() code, but using str(user) as the dictionary key looks fragile. It would probably be better to use user.pk or user.username instead.
I have an issue with my Create view. I initialise it like this:
class OutputCreateView(LoginRequiredMixin, generic.CreateView):
template_name = 'rcapp/common_create_update.html'
form_class = OutputForm
model = Output
def get_context_data(self, **kwargs):
context = super(OutputCreateView, self).get_context_data(**kwargs)
# self.form_class.fields['activity_ref'].queryset = Activity.objects.filter(rc_ref=ResultsChain.objects.get(pk=self.kwargs['rc']).pk)
context['is_authenticated'] = self.request.user.is_authenticated
return context
def form_valid(self, form):
# code code code
return redirect("/portal/edit/" + str(self.kwargs['rc']) + "/#outputs-table")
I have a ForeignKey Field in my model and I wanted to filter options for current view.
My form is set like this:
class OutputForm(forms.ModelForm):
class Meta:
model = Output
fields = ['value', 'activity_ref']
widgets = {
'value': forms.Select(choices=(#Choises here
,), attrs={"onChange":'select_changed()', 'class':'selector'})
}
I need to change a queryset for the activity_ref field.
You can see a commented line in get_context_data, it's where I tried to do this. But it didn't work. How can I get what I need?
You need to pass the choices / queryset to your form.
in OutputCreateView
def get_form_kwargs(self, *args, **kwargs)
filter_key = self.kwargs['rc']).pk
return {'filter_key': key}
Like this, it will give an error in when your form gets created, because of the unexpected argument. To get around that and to make use of it, override the init method.
In your OutputForm
def __init__(self, *args, **kwargs)
kwargs.pop('filter_key')
super()._init__(*args, **kwargs)
self.fields['value'] = forms.Select(queryset=Activity.objects.filter(rc_ref=ResultsChain.objects.get(pk=self.kwargs['rc']).pk),
attrs={"onChange":'select_changed()', 'class':'selector'})
You don't need to set the widgets value, as it is being done in the init method.
I have a view that has 3 sections. The top section is a search area. The bottom left section shows a list of objects and the right section shows the details of the selected object.
My view inherits from ListView and FormView.
class SearchViewMixin(FormView):
template = 'template here'
form_class = forms.SearchForm
def get_form_kwargs(self):
print 'This is not being called'
kwargs = super(SearchViewMixin, self).get_form_kwargs()
kwargs['search-units'] = self.request.search.unit_set.all()
return kwargs
class ListView(ListView, SearchViewMixin):
def get_queryset()...
def get_context_data()....
The form view is this:-
class ReceiptSearchForm(BaseForm):
obj_id = forms.IntegerField(required=False)
unit = forms.ModelChoiceField(queryset=None, required=False, empty_label='Select a unit',
widget=core.SelectPickerSearch)
def __init__(self, *args, **kwargs):
self.units = kwargs.pop('search-units', None)
super(ReceiptSearchForm, self).__init__()
self.fields['unit'].queryset = self.units
The problem is the get_form_kwargs method is not being called. The print statement is not getting executed
Since I am not writing or updating any data, I am just using a get method for the form.