I want to display all elements of a form including the pk and I have not found a satisfying method to do so:
class SomeForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["number"] = forms.IntegerField(required = True)
self.fields["id"] = forms.IntegerField(disabled = True)
self.fields["data"] = forms.CharField(required = False)
class Meta:
model = SomeModel
fields = ["id", "number", "data"]
So now I just call the form in the view with:
class SomeView(TemplateView):
template_name = "app/sometemplate.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = MODEL.objects.get(pk = context["pk"])
MODEL_data = {}
MODEL_data["number"] = obj.number
MODEL_data["id"] = obj.pk
MODEL_data["data"] = obj.data
context["form"] = SomeForm(initial = MODEL_data)
return context
and now in the templatetags I have a filter get_pk:
#register.filter(name='get_pk')
def get_pk(obj):
return obj.initial["id"]
and I get the pk in the template with {{ form|get_pk }} and I feel like this is not smart. I tried stuff like {{ form.instance.id }} like suggested here. I still feel there must be an easy way to achieve this?
I don't know why you need form in template view? You just need to get the context no more..
There are many comments in shared code like:
1-Disabled id field in initial form
2-Using different model name in form and get context data
3-Filtering context by using context["pk"] before assessing obj
Find the revised code,
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["obj"] = SomeModel.objects.get(pk = the desired value)
return context
Note: if you want user to insert pk then you can use input field inside form with get method and get its value inside get context data in this way:
context["pk"] = self.request.GET.get("name of input field ")
In template html you can display fields directly:
{{obj.id}},{{obj.name}}, {{obj.data}}
Related
I'm currently trying to convert my FBV codes to CBV. get_context_data is working well by returning contexts that I put in. However, get_queryset() returns NOTHING for some reason. To double-check, I tried to print search_stores right before returning it and it printed the queryset that is supposed to be printed. However, when I printed it on Django template, by typing {{ search_stores }}, it shows nothing. Am I using get_queryset in a wrong way?
class SearchListView(ListView):
model = Store
template_name = 'boutique/search.html'
# paginate_by = 5
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_text'] = self.request.GET.get('search_text')
context['sorter'] = self.request.GET.get('sorter')
if not context['sorter']:
context['sorter'] = 'popularity'
return context
def get_queryset(self):
search_text = self.request.GET.get('search_text')
sorter = self.request.GET.get('sorter')
if search_text:
search_stores = Store.objects.filter(Q(businessName__icontains=search_text) | Q(mKey__icontains=search_text))
if sorter == 'businessName':
search_stores = search_stores.order_by(sorter)
else:
search_stores = search_stores.order_by(sorter).reverse()
else:
search_stores = ''
for store in search_stores:
store.mKey = store.mKey.split(' ')
print(search_stores)
return search_stores
Your queryset is accessible via the context_object_name.
By default it's object_list if you don't provide context_object_name
You can access the queryset in templates with object_list
If you want to change the name, change the context_object_name:
class SearchListView(ListView):
model = Store
template_name = 'boutique/search.html'
context_object_name = 'search_stores'
search_stores will be the variable accessible to loop through in templates
I have a FormView from which I'd like to do a bit of form processing.
Within the form_valid() I'm trying to obtain the submitted form's values in order to instantiate a different object.
When I get the form.cleaned_data however, I am returned a dictionary of {'form field name' : 'choice label'}.
I'd like to get the value corresponding to the choice label.
Here is the FormView--the get_form_kwargs bit is simple passing custom choices to the view:
class RequestView(FormView):
form_class = RequestForm
template_name = 'profile/request.html'
success_url = '/'
def get_form_kwargs(self, *args, **kwargs):
requester_obj = Profile.objects.get(
user__username=self.request.user)
accepter_obj = Profile.objects.get(
user__username=self.kwargs.get('username'))
r_favorite_set = requester_obj.get_favorite()
a_favorite_set = accepter_obj.get_favorite()
kwargs = super().get_form_kwargs()
kwargs['your_favorite'] = r_favorite_set
kwargs['their_favorite'] = a_favorite_set
return kwargs
def form_valid(self, form):
super(RequestView, self).form_valid(form)
# I've tried get_FOO_display() extra instance method
print(form.get_your_favorite_display())
# The following will return the choice label
print(form.cleaned_data.get('your_favorite'))
Favorites.objects.create(#need form choice selections)
return redirect(self.get_success_url())
Code explanation: Within the form_valid I hope to create a different object using the selection from the submitted form.
So how do I get the submitted choice instead of the label? Is this even the proper place to do this?
edit: The Form:
class RequestForm(forms.Form):
your_favorite = forms.ChoiceField(
choices=[],
widget=RadioSelect,
required=True,
label="What would you like to exchange?"
)
their_favorite = forms.ChoiceField(
widget=RadioSelect,
required=False,
)
def __init__(self, *args, **kwargs):
your_choices = kwargs.pop('your_favorite')
their_choices = kwargs.pop('their_favorite')
super().__init__(*args, **kwargs)
self.fields['your_favorite'].choices = your_choices
self.fields['their_favorite'].choices = their_choices
edit2:
def get_favorite(self):
return (('a', self.fave1), ('b', self.fave2), ('c', self.fave3))
edit3:
...So I could've just done this all along.
def get_favorite(self):
return ((self.fave1, self.fave1), (self.fave2, self.fave2), (self.fave3, self.fave3))
BUT this causes some really funky behavior in the form. For some reason this causes a radio to be selected by default with every GET request to the form view. It will select the third choice to be selected if all the options are "None", or the first option to be selected if only the first is "None".
Anyway, this may be a question for a separate post.
I have a model say Club where we have fields like:
manager = models.ForeignKey(Users, related_name="return_manager", on_delete=models.CASCADE)
members = models.ManyToManyField(Users, related_name="return_members", blank=True)
Now I want to create a drop down in a form where I can add both the manager and members to it. I tried making two requests for Club.objects.filter(pk=mypk).members.all() and Club.objects.filter(pk=mypk).manager. I tried chain function and using '| ' operator but none worked. I think the manager is a single User and not a queryset, that is what the main problem is. Any workarounds?
One possible way getting all of the information together involves modifying your form choices.
In your view you would need to pass the choices along as context to your form.
def my_view(request, club_pk):
context = {}
context.update({
"manager": Club.objects.get(pk=club_pk).manager,
"members": Club.objects.get(pk=club_pk).members.all()
}
form = MyForm(request.POST or None, request=request, context=context)
In your form, you would need to modify the __init__ method to update your choices like so:
class MyForm(forms.Form):
all_club_members = forms.ChoiceField('Manager + Members', required=True)
def __init__(self, *args, **kwargs):
self.context = kwargs.pop('context', None)
super(MyForm, self).__init__(*args, **kwargs)
manager_tuple = [(self.context['manager'].id, self.context['manager'].display_name)]
members_tuples = [(member.id, member.display_name) for member in self.context['members']
self.fields['all_club_members'].choices = manager_tuple + members_tuples
Try this:
manager = [Club.objects.filter(pk=mypk).manager]
members = Club.objects.filter(pk=mypk).members.all()
userlist = list(manager) + list(members)
return Users.objects.filter(pk__in=userlist)
Should create a queryset of all users
My Goal
A site that list all my Updates (model) in a table
Dont display all models at once (pagination - maybe 10 per page)
Filter and sort the list
My thoughts
I can use ListView to get a set of all my Updates
Use paginate_by = 10
Use a form to set order_by or filter in my QuerySet
My Problem
I am not sure how to add an form to modify my QuerySet with filter and sortings. My Idea was to modify the Query in get_queryset with additional filter and order_by.
My View
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
My Idea
Something like this. I know it's not working like this ... just to illustrate
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def post(self, request, *args, **kwargs):
new_context = Update.objects.filter(
request.POST.get("filter"),
).order_by(
request.POST.get("sorting"),
)
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
You don't need post. Pass the filter value and order_by in the url for example:
.../update/list/?filter=filter-val&orderby=order-val
and get the filter and orderby in the get_queryset like:
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
filter_val = self.request.GET.get('filter', 'give-default-value')
order = self.request.GET.get('orderby', 'give-default-value')
new_context = Update.objects.filter(
state=filter_val,
).order_by(order)
return new_context
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['filter'] = self.request.GET.get('filter', 'give-default-value')
context['orderby'] = self.request.GET.get('orderby', 'give-default-value')
return context
Make sure you give proper default value to filter and orderby
Example form (you can modify this to your need):
<form method="get" action="{% url 'update-list' %}">
<p>Filter: <input type="text" value={{filter}} name="filter"/></p>
<p>order_by: <input type="text" value={{orderby}} name="orderby"/></p>
<p><input type="submit" name="submit" value="submit"/></p>
</form>
I am wondering why nobody mentioned here this cool library: django-filter https://github.com/carltongibson/django-filter
you can define your logic for filtering very clean and get fast working forms etc.
demo here: https://stackoverflow.com/a/46492378/953553
I posted this elsewhere but I think this adds to the selected answer.
I think you would be better off doing this via get_context_data. Manually create your HTML form and use GET to retrieve this data. An example from something I wrote is below. When you submit the form, you can use the get data to pass back via the context data. This example isn't tailored to your request, but it should help other users.
def get_context_data(self, **kwargs):
context = super(Search, self).get_context_data(**kwargs)
filter_set = Gauges.objects.all()
if self.request.GET.get('gauge_id'):
gauge_id = self.request.GET.get('gauge_id')
filter_set = filter_set.filter(gauge_id=gauge_id)
if self.request.GET.get('type'):
type = self.request.GET.get('type')
filter_set = filter_set.filter(type=type)
if self.request.GET.get('location'):
location = self.request.GET.get('location')
filter_set = filter_set.filter(location=location)
if self.request.GET.get('calibrator'):
calibrator = self.request.GET.get('calibrator')
filter_set = filter_set.filter(calibrator=calibrator)
if self.request.GET.get('next_cal_date'):
next_cal_date = self.request.GET.get('next_cal_date')
filter_set = filter_set.filter(next_cal_date__lte=next_cal_date)
context['gauges'] = filter_set
context['title'] = "Gauges "
context['types'] = Gauge_Types.objects.all()
context['locations'] = Locations.objects.all()
context['calibrators'] = Calibrator.objects.all()
# And so on for more models
return context
This is how we do it, that way you get validation/type conversion as well:
class UnitList(PermissionRequiredMixin, ListView):
""" Class based view to show a list of all buildings for a specific user """
model = Unit
ordering = ['building', 'unit']
paginate_by = 100
# Access
permission_required = ['core.manager_perm']
raise_exception = True # If true, give access denied message rather than redirecting to login
def get_queryset(self):
try:
units = self.model.objects.filter(building__company=self.request.user.profile.company)
except Profile.DoesNotExist:
units = self.model.objects.none()
form = UnitSearchForm(self.request.GET)
if form.is_valid():
filters = {}
address = form.cleaned_data['address']
neighborhood = form.cleaned_data['neighborhood']
beds = form.cleaned_data['beds']
amenity = form.cleaned_data['amenity']
if address:
filters['building__street_index__istartswith'] = compute_street_address_index(address)
if neighborhood:
filters['building__neighborhood__icontains'] = neighborhood
if beds:
filters['beds'] = beds
if amenity:
filters['unit_amenities__name__iexact'] = amenity
units = units.filter(**filters)
return units.select_related('building').order_by(*self.ordering)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = UnitSearchForm(self.request.GET)
return context
My website helps musicians connect and borrow/lend their instruments from/to one another.
I have a form on my webpage called InstrumentSearchForm which let's you search for an instrument by category, date and location.
class InstrumentSearchForm(forms.Form):
categories = forms.MultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple())
date = forms.DateField(required=False)
location = forms.CharField(required=False)
This form is initialized in the view and passed to the template in the context (I caught out things that were unrelated clutter)
def main_page(request):
if request.method == 'POST':
....
else:
form_search = InstrumentSearchForm(prefix="search") # An unbound form
args = {}
args.update(csrf(request))
args['form_search'] = form_search
...
categories_to_show = Categories.objects.filter(users = request.user) #show users categories
form_search.fields['categories'].queryset = categories_to_show
return render(request, 'main_page.html', args)
The trouble is, that in the template page, when I say
{{ form_search }}
the form is missing the "Categories" widget. It only has the date and location boxes. In the source, it doesn't show any choices for Categories even though I know they exist.
I've been trying to figure out what the problem, with no results. Does anyone have any ideas?
I can't see anything necessarily wrong with the code you've posted, but I would put the logic into the form:
class InstrumentSearchForm(forms.Form):
categories = forms.MultipleChoiceField(queryset=Categories.objects.none(), required=False, widget=forms.CheckboxSelectMultiple())
...
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(InstrumentSearchForm, self).__init__(*args, **kwargs)
self.fields['categories'].queryset = Categories.objects.filter(users=user)
and instantiate it with:
form = InstrumentSearchForm(user=request.user)
Does that help at all?