Passing arguments to Django CBV for queryset - django

I'm trying to figure out how can I pass arguments from a generic view where a user submits a form to a cbv template view.
My first generic view is as such:
def homepage(request):
if request.method == 'POST':
form = SearchForm(request.POST)
if form.is_valid():
form_data = form.cleaned_data
return redirect('filtered', city=form_data['city'], category=form_data['category'])
else:
form = SearchForm()
return render(request, 'index.html', context={'form': form})
What Im trying to achieve is that when a user choses a city and a category then he gets submitted to a homepage that is a TemplateView where there's a queryset based on objects that are in the selected city and of selected category.
class ListAd(ListView):
model = Ad
template_name = 'filtered'
queryset = # here Im expecting to make a query based on the 'city' and 'category' chosen in previous view
How to pass the city and category arguments to the ListAd view so I can use them in the queryset? This is the ursl.py
urlpatterns = [
path('list_new/<city>/<category>/', views.ListAd.as_view(), name='filtered'),
]
Any help would be appreciated. Thank you

I think that this should works.
class ListAd(ListView):
model = Ad
template_name = 'filtered'
queryset = Ad.objects.all()
def get_queryset(self):
queryset = super(ListAd, self).get_queryset()
queryset = queryset.filter(city=self.kwargs["city"], category=self.kwargs["category"])
return queryset

Related

How can I render a Django Formset with some data for update process?

I am using Dajngo formset with inlineformset_factory
What I need it when the user click in the update like It should render formset with the value.
This is the code I did but nothing works:
This is how I create a formset
OrderItemFormset = inlineformset_factory(
Order, OrderItem, fields='__all__', extra=1, can_delete=False)
And here how I tried to render the formset with the queryset=my_query_set.
if 'id' in kwargs.keys():
order = Order.objects.get(id=kwargs.get('id'))
order_items = OrderItem.objects.filter(order_id=kwargs.get('id'))
else:
order = None
order_items = None
order_form = OrderForm(instance=order)
print(order_items)
order_item_form = OrderItemFormset(queryset=order_items)
When I click in the update link Django render the Parent form with the data I need, but this does not work with formset it just give me one row with empty data.
After some trying to solve my problem I found that I use queryset, and I think I should use instance instead.
And the other thing is that the instance was from OrderItem model and instead it should be from the Order model so the code should look like this.
def get(self, request, *args, **kwargs):
if 'id' in kwargs.keys():
order = Order.objects.get(id=kwargs.get('id'))
else:
order = None
order_form = OrderForm(instance=order)
order_item_form = OrderItemFormset(instance=order)
orders = Order.objects.all()
context = {'order_form': order_form,
'order_item_form': order_item_form, 'orders': orders}
return render(request, self.template_name, context)

<News:somethinng>" needs to have a value for field "id" before this many-to-many relationship can be used

Here from my template I am getting the list of strings with getlist in a view. I want to assign these list in my ManyToMany field.I am getting the selected multiple list in my view and it is also creating the reporter object from these list which is fine.
Now I want to assign these only selected multiple options in my many to many field.
How can I do it here? I get this error while saving the form
"<News:somethinng>" needs to have a value for field "id" before this many-to-many relationship can be used.
views
class NewsCreateVView(View):
template_name = 'add_news.html'
def get(self, request):
form = CreateNewsForm()
return render(request, self.template_name, {'form': form})
def post(self, request, **kwargs):
form = CreateNewsForm(request.POST)
reporters = request.POST.getlist('reporter')
if form.is_valid():
news = form.save(commit=False)
for reporter in reporters:
obj = Reporter.objects.create(name=reporter)
news.reporter.add(obj.pk)
news.save()
return redirect('list_news')
models
class Reporter(models.Model):
name = models.CharField(max_length=255)
created = models.DateTimeField(auto_now_add=True)
class News(models.Model):
title = models.CharField(max_length=255)
reporter = models.ManyToManyField(Reporter, related_name='reporters')
template
<select class="form-control" name="reporter" multiple="multiple">
As the error says, news first needs to be saved in order to have a primary key, so:
class NewsCreateVView(View):
template_name = 'add_news.html'
def get(self, request):
form = CreateNewsForm()
return render(request, self.template_name, {'form': form})
def post(self, request, **kwargs):
form = CreateNewsForm(request.POST)
reporters = request.POST.getlist('reporter')
if form.is_valid():
news = form.save()
pks = [Reporter.objects.create(name=reporter).pk
for reporter in reporters]
news.reporters.add(*pks)
return redirect('list_news')

Creating UpdateForm by using ModelForm in Django

I have a Django application and I wanna create an update form with ModelForm but I have to get the fields parameter from user_profile model. So I have created my form and view such as below.
forms.py;
class UpdateForm(forms.ModelForm):
class Meta:
model = Data
fields = []
def __init__(self, *args, **kwargs):
input_choices = kwargs.pop('input_choices')
super(UpdateForm, self).__init__(*args,**kwargs)
self.fields = input_choices
views.py;
#login_required(login_url = "user:login")
def updateData(request,id):
features = request.user.profile.selected_features
instance = get_object_or_404(Data,id = id)
form = UpdateForm(request.POST or None, input_choices=features, instance=instance)
if form.is_valid():
data = form.save(commit=False)
data.author = request.user
data.save()
return redirect("data:dashboard")
return render(request,"update.html",{"form":form})
However, I'm getting a list indices must be integers or slices, not str error in the return render(request,"update.html",{"form":form}) line. There is an issue with the fields parameter but I couldn't find anything.
How can I pass the fields parameter from view to ModelForm?
You can use modelform_factory to create a form with dynamic fields
from django.forms import modelform_factory
#login_required(login_url = "user:login")
def updateData(request,id):
features = request.user.profile.selected_features
instance = get_object_or_404(Data, id=id)
UpdateForm = modelform_factory(Data, fields=features)
form = UpdateForm(request.POST or None, instance=instance)
If features is a queryset, you'll probably have to use something like features.values_list('name', flat=True) to get just the names of the fields

"Select a valid choice. <choice> is not one of the available choices" error when submitting ManyToMany ModelForm

I want to limit the choices of a ManyToManyField to those matching a ForeignKey. The form displays properly, but upon saving results in an error Select a valid choice. <choice> is not one of the available choices.
Before I was trying to limit the queryset by passing a parameter in the view to the form, and then using that parameter to filter the queryset.
Models:
class VenueEventTimeslot(models.Model):
venue = models.ForeignKey(Venue)
name = models.CharField(max_length=255)
class VenueEvent(models.Model):
venue = models.ForeignKey(Venue)
event_timeslots = models.ManyToManyField(VenueEventTimeslot)
class VenueEventForm(ModelForm):
event_timeslots = ModelMultipleChoiceField(queryset=None, widget=CheckboxSelectMultiple())
def __init__(self, *args, **kwargs): # limit timeslots to those of the venue only
venue_obj = kwargs.pop('venue_obj',None)
super(VenueEventForm, self).__init__(*args,**kwargs)
self.fields['event_timeslots'].queryset=VenueEventTimeslot.objects.filter(venue=venue_obj)
class Meta:
model = VenueEvent
fields = ['event_timeslots']
Views:
#login_required
def calendar(request, pk):
venue = Venue.objects.get(pk = pk)
if request.method == "POST":
form = VenueEventForm(request.POST)
if form.is_valid():
# form stuff
else:
form = VenueEventForm(venue_obj = venue)
context = {'venue':venue, 'form':form}
return render(request, ... , context)
However, if I pass the queryset from the view, it works perfectly.
Models:
class VenueEventTimeslot(models.Model):
# same as above
class VenueEvent(models.Model):
# same as above
class VenueEventForm(ModelForm):
class Meta:
model = VenueEvent
fields = ['date','client_name','event_timeslots']
widgets = {
'date': SelectDateWidget(),
'event_timeslots': CheckboxSelectMultiple(),
}
Views:
#login_required
def calendar(request, pk):
venue = Venue.objects.get(pk = pk)
if request.method == "POST":
form = VenueEventForm(request.POST)
if form.is_valid():
# form stuff
else:
form = VenueEventForm()
form.fields['event_timeslots'].queryset=VenueEventTimeslot.objects.filter(venue=venue)
context = {'venue':venue, 'form':form}
return render(request, ..., context)
Would anyone be able to shed some light on this?
I just solved a problem similar to this yesterday which is right here, How To Exclude A Value In A ModelMultipleChoiceField?, but I think the issue with your init function is the way it is formatted. Instead of venue=venue_obj, you need to change it to pk=venue_obj because it appear you are getting the pk of venue in the view instead of the venue attribute of VenueEvent , and I reformatted your form a bit to make it look cleaner.
forms.py
class VenueEventForm(ModelForm):
def __init__(self, *args, **kwargs): # limit timeslots to those of the venue only
venue_obj = kwargs.pop('venue_obj')
super(VenueEventForm, self).__init__(*args,**kwargs)
self.fields['event_timeslots'] = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=VenueEventTimeslot.objects.filter(pk=venue_obj))
class Meta:
model = VenueEvent
fields = ['event_timeslots']
views.py
#login_required
def calendar(request, pk):
venue = Venue.objects.get(pk = pk)
if request.method == "POST":
form = VenueEventForm(request.POST, venue_obj=venue)
if form.is_valid():
# form stuff
else:
print VenueEventForm.errors
else:
form = VenueEventForm(venue_obj=venue)
context = {'venue':venue, 'form':form}
return render(request, ... , context)

using filters with django-endless-pagination

I'm using Django endles-pagination to load the pages in infinite scroll. I also have some filters that filter the data according to the criteria (for eg, price slider filtering according to price). Now when the page loads, the filter right now filters only from the page loaded, though I want it to filter it from all the pages that have been or are to be loaded. Is there a way to do this (by making some ajax request or something)?
Any help on this would be great. Thanks a lot.
To filter the data you have to redefine get_queryset() method in the views requesting the filtered query.
For example I request the current language in template to filter the Blog posts based on the language:
class Blog(AjaxListView):
context_object_name = "posts"
template_name = 'cgapp/blog.html'
page_template = 'cgapp/post_list.html'
def get_queryset(self):
if self.request.LANGUAGE_CODE == 'en': #request value of the current language
return News.objects.filter(language='en') #return filtered object if the current language is English
else:
return News.objects.filter(language='uk')
To filter the queryset based on the users input, you may refer to POST method:
from app.forms import BlogFilterForm
class Blog(LoginRequiredMixin, AjaxListView):
context_object_name = "posts"
template_name = 'blog/blog.html'
page_template = 'blog/post_list.html'
success_url = '/blog'
def get_queryset(self): # define queryset
queryset = Post.objects.all() # default queryset
if self.request.method == 'POST': # check if the request method is POST
form = BlogFilterForm(self.request.POST) # define form
if form.is_valid():
name = form.cleaned_data['name'] # retrieve data from the form
if name:
queryset = queryset.filter(name=name) # filter queryset
else:
queryset = queryset
return queryset
def get_context_data(self, **kwargs):
context = super(Blog, self).get_context_data(**kwargs)
context['form'] = BlogFilterForm() # define context to render the form on GET method
return context
def post(self, request, *args, **kwargs): # define post method
return super(Blog, self).get(request, args, kwargs)
The endless pagination should work fine.