Hi guys I'm building a simple django website where the user can look for long play. The problem right now is that I can't figure out why the filter, built with the django_filters library, doesn't work.
If you type "Queen" in the search bar in the home you are redirected to the searches.html where are listed all the LPs by Queen (just 4). On the left there's a filter. If you type "kind" in the form and submit then you should just see the album "Kind of Magic". This doesn't work. And I think the problem is the queryset "sp":
class searchesView(TemplateView):
template_name = "search/searches.html"
def post(self, request, *args, **kwargs):
print('FORM POSTED WITH {}'.format(request.POST['srh']))
srch = request.POST.get('srh')
if srch:
sp = Info.objects.filter(Q(band__icontains=srch)) | Info.objects.filter(Q(disco__icontains=srch))
myFilter = InfoFilter(request.GET, queryset=sp)
sp = myFilter.qs
paginator = Paginator(sp, 10)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(self.request, 'search/searches.html', {'sp':sp, 'myFilter':myFilter,
'page_obj': page_obj
})
else:
return render(self.request, 'search/searches.html')
I think the problem is the queryset because then I tried to build a result.html page where I listed all the LPs in the database and applied the same filter above and here it works perfectly:
def result(request):
result = Info.objects.all()
myFilter = InfoFilter(request.GET, queryset=result)
result = myFilter.qs
return render (request, 'search/result.html', {'result':result, 'myFilter':myFilter})
The difference is that in the first case I've got a TemplateView with a certain queryset, and in the second case I've got a simple function with a different queryset. Someone know what the problem could be? I uploaded to code on repl if you want to try out the website: https://Django-Template.edwardradical.repl.co
Thanks a lot!
Issue has to do with the mix of POST/GET here.
You are reading parameters out of both post/get dictionaries here, which is not a great pattern to go down.
In this case, a POST request to the url with the filters should yield the correct results ie:
POST /searches/?band=kind&disco=
{
srh: data
}
In current solution, I believe the srh is not included in the post body, which causes issue in queryset.
Related
I have a url, http://127.0.0.1:8000/lesson/riff-lab/1305/pentab-wow/
When a user navigates to the above url, I want to change it to http://127.0.0.1:8000/lesson/riff-lab/1305/pentab-wow/?d:a3ugm6eyko59qhr/pentab-Track_1.js
The appended part is needed in order to load something that I want to load, but the specifics are not important for this question.
Here's what I have tried.
def my_view(request, pk):
context = {}
page = Page.objects.get(pk=pk)
request.GET._mutable = True
request.GET['?d:%s/%s' % (page.dropbox_key, page.dropbox_js_file_name)] = ""
return render(request, template, context)
Also
def my_view(request, pk):
context = {}
page = Page.objects.get(pk=pk)
request.GET = request.GET.copy()
request.GET['?d:%s/%s' % (page.dropbox_key, page.dropbox_js_file_name)] = ""
return render(request, template, context)
These do not change the url.
Can anyone help? Thanks in advance.
You trying to change the aim of a shell that already has hit it's target.
URL comes first, then view routed to it is processed, there is no way to change it without making another request, e.g. returning a redirect response "please open this url now" to the client.
You can easily find it in django docs by this keywords, but what you are trying to do generally doesn't look very reasonable, if you know beforehand how you need to construct url, why change it mid-way or why change it at all if view has all required data? I don't know your context, but it's probable that you need to reconsider your approach.
I am Building a BlogApp and I implement a feature to count the views of Post. BUT views are not showing in post page in Browser.
What i am trying to do
I am trying to count the number of views which post will got whenever the user visits.
The Problem
Post views are not showing in post page in browser.
What have i tried
1). I also tried models.IntegerField BUT that didn't worked for me , because whenever user refresh the page then it increase one view everytime for single user.
2). I followed some tutorials BUT the all of them were on Class Based Views and I am using Function Based Views.
3). Then i thought of IP address BUT that also didn't worked for me because it wasn't working for my server.
Then i think of a ManyToManyField in views variable. BUT it also not working for me, I don't know where is the problem.
views.py
def detail_view(request,pk):
data = get_object_or_404(BlogPost,pk=pk)
queryset = BlogPost.objects.order_by('viewers')
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
models.py
class BloPost(models.Model):
post_owner = models.ForeignKey(User,default='',null=True,on_delete = models.CASCADE)
date_added = models
viewers = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='viewed_posts',editable=False)
show_more.html
1). This is showing auth.User.None
{{ data.viewers }}
2). This is showing all the posts.
{{ queryset }}
I don't what am i doing wrong.
Any help would be appreciated.
Thank You in Advance.
You should annotate your queryset, so:
from django.db.models import Count
def detail_view(request,pk):
queryset = BlogPost.objects.annotate(
num_views=Count('viewers')
).order_by('-num_views')
data = get_object_or_404(queryset, pk=pk)
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
and then you can render this with:
{{ data.num_views }}
If you want to add the user to the viewers, you can run the logic to add the user to the viewers:
from django.db.models import Count
def detail_view(request,pk):
queryset = BlogPost.objects.annotate(
num_views=Count('viewers')
).order_by('-num_views')
data = get_object_or_404(queryset, pk=pk)
if self.user.is_authenticated:
__, created = Post.viewers.through.objects.get_or_create(
post=data,
user=self.request.user
)
if created:
data.num_views += 1
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
That being said, this to some extent demonstrates that function-based views are often not well-suited for a view that requires to run logic that consists out of a number of separate parts. In that case it is better to work with mixins and define a class-based view in terms of these mixins.
So currently I'm on a page with a url link like this
urls.py :
path('key/<int:pk>', views.KeyDetailView.as_view(), name='roomkey-detail'),
views.py :
class KeyDetailView(generic.DetailView):
model = RoomKey
this lists out a list of keys available to be borrowed for a particular room. Then when I try to head to the next page, where is a request I can make to borrow out one of the keys for that room, Here is the urls and views responsible for rendering the roomkey-request page
urls.py :
path('key/<int:pk>/request', views.KeyRequestCreate.as_view(), name='roomkey-request')
views.py :
class KeyRequestCreate(CreateView):
model = KeyRequest
fields = ['roomkey', 'requester', 'borrower', 'request_comments']
template_name = 'catalog/roomkey_request_form.html'
there is a button that on that page that links to a terms and agreement page that looks like this
roomkey_request_form.html :
terms and conditions
urls.py :
path('key/<int:pk>/request/agreement', views.KeyAgreement, name='key-agreement'),
views.py :
def KeyAgreement(request):
return render(
request,
'catalog/roomkey_agreement.html',
)
however when try to click on that request button to request a key, django throws an error
NoReverseMatch at /catalog/key/2/request
Reverse for 'key-agreement' with arguments '('',)' not found. 1 pattern(s)
tried: ['catalog\\/key\\/(?P<pk>[0-9]+)\\/request\\/agreement$']
I have a button on the terms and agreement to go back to the request page something that looks like this
<button href="{% url 'roomkey-request' roomkey.pk %}" >Return to request</button>
will this return to the request page with the correct pk? I think i'm just confused with how the url handles pk and how it get's passed on.I am thinking this had to do with something the keyagreement not being able to take in that pk from the details page, can someone please explain to me what I am doing wrong or point me to some resource that can help me understand how urls pass along the pk from view to view? I am fairly new to django so thanks for your help in advance!!
Try:
def KeyAgreement(request, pk): #-->pk in argument
return render(
request,
'catalog/roomkey_agreement.html',
)
If you want to use roomkey.pk in the roomkey_request_form.html template, you need to add roomkey to the template context. You can do this in the get_context_data method.
Since you already have the roomkey pk from the URL, you can remove it from fields. Then set roomkey in the form_valid method before saving.
class KeyRequestCreate(CreateView):
model = KeyRequest
fields = ['requester', 'borrower', 'request_comments']
template_name = 'catalog/roomkey_request_form.html'
def get_context_data(self, **kwargs):
context = super(KeyRequestCreate, self).get_context_data(**kwargs)
context['roomkey'] = get_object_or_404(RoomKey, pk=pk)
def form_valid(self, form):
form.instance.roomkey = get_object_or_404(RoomKey, pk=pk)
return super(KeyRequestCreate, self).get_form(form)
If you want to use roomkey in the agreement view, you'll have to make some changes to it as well.
First, you need to add pk to the function signature since you have <int:pk> in its URL pattern.
Then, you need to include roomkey in the template context.
from django.shortcuts import get_object_or_404
def key_agreement(request, pk):
roomkey = get_object_or_404(roomkey, pk=pk)
return render(
request,
'catalog/roomkey_agreement.html',
{'roomkey': roomkey}
)
Note that I've renamed the view function to key_agreement to match the recommended style. You'll need to update the URL pattern as well.
path('key/<int:pk>/request/agreement', views.KeyAgreement, name='key-agreement'),
Context
I'm handling a form in a python view. Basic stuff.
def index(request):
# Handle form.
if request.method == 'POST':
form = CustomForm(request.POST)
if form.is_valid():
# Do stuff
return HttpResponseRedirect('/thankyou/')
else:
form = CustomForm()
# Render.
context = RequestContext(request, {
'form':form,
})
return render_to_response('app/index.html', context)
This form is shown on multiple pages, and I've ended up having duplicates of the form-handling code in multiple functions in views.py, rendering different templates. (However, the template code for the form resides in the base template)
That's dumb, so I tried looking around for ways to prevent the repeat of code. I like the suggested use of python decorators in this Stackoverflow question. I also found an excellent explanation of python's decorators here.
Question
I'm having trouble with trying to write the decorator. I need to return a form after the first if statement, followed by executing another if statement. But in a python function, no code after a return function gets executed... Does this require something like a nested decorator..?
Suggestions? Non-decorator suggestions welcome.
This is not the answer to your main question but this info may be helpful to you or somebody.
The question with suggestion about decorators is pretty old. Started from 1.3 version django have class based views - i think this is what you are looking for. By subclassing views you can reduce duplication of code (code from django docs just for example):
# Base view
class MyFormView(View):
form_class = MyForm
initial = {'key': 'value'}
template_name = 'form_template.html'
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
return render(request, self.template_name, {'form': form})
Now you can create another views classes based on MyFormView view. Form processing code stays same, but you can change it of course:
class AnotherView(MyFormView):
form_class = AnotherForm
initial = {'key1': 'value1'}
template_name = 'form1_template.html'
# you dont need to redefine post here if code stays same,
# post from base class will be used
Is there a way I can pass data from a form submission over to the 'thank you' page. The reason i'd like to do this is because I have a form on the website, where the user will select multiple fields which all contains different PDF's.
So once the user has submitted the form the idea is to re-direct them to a thankyou page, where they can view the list of pdf/files they have selected on the form.
I hope this is enough info to go on. Here are my views / models.
def document_request(request, *args):
# template = kwargs['name'] + ".html"
if request.method == 'POST':
form = forms.ReportEnquiryForm(request.POST)
print(request.POST)
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
else:
form = forms.ReportEnquiryForm()
return render_to_response('test.html',{'form':form})
def thank_you(request):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
My initial idea was to pass the data to a new view called thank_you. But not this is possible.
class DocumentUpload(models.Model):
name = models.CharField(max_length="200")
document_upload = models.FileField(upload_to="uploads/documents")
def __unicode__(self):
return "%s" % self.name
class DocumentRequest(models.Model):
name = models.CharField(max_length="200")
company = models.CharField(max_length="200")
job_title = models.CharField(max_length="200")
email = models.EmailField(max_length="200")
report = models.ManyToManyField(DocumentUpload)
def __unicode__(self):
return "%s" % self.name
form.py
class ReportEnquiryForm(forms.ModelForm):
class Meta:
model = models.DocumentRequest
fields = ('name', 'company', 'job_title', 'email', 'report')
If you need anymore info, please ask :)
You've saved the user's submission in a DocumentRequest object. So you can pass the ID of that object in the URL when you redirect, and in the thank_you view you can get the DocumentRequest and render the list.
Edit The idea is to make the thank_you page like any other view that accepts a parameter from the URL:
url(r'thanks/(?P<id>\d+)/$, 'thank_you', name='thank_you')
and so the POST part of the form view becomes:
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
and thank_you is:
def thank_you(request, id):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
Second edit
As others have suggested, this makes it possible for anyone to see the request. So a better solution is to put it in the session:
docrequest = form.save()
request.session['docrequest_id'] = docrequest.id
and in thank_you:
def thank_you(request):
if not 'docrequest_id' in request.session:
return HttpResponseForbidden
docrequest = DocumentRequest.objects.get(request.session['docrequest_id'])
You can do as Daniel Roseman said but in this case the thank you pages can be accessed by anyone with the Ids.
Some ways to pass data between views are the following(the list is not mine.):
GET request - First request hits view1->send data to browser -> browser redirects to view2
POST request - (as you suggested) Same flow as above but is suitable when more data is involved
Using django session variables - This is the simplest to implement
Using client-side cookies - Can be used but there is limitations of how much data can be stored.
Maybe using some shared memory at web server level- Tricky but can be done.
Write data into a file & then the next view can read from that file.
If you can have a stand-alone server, then that server can REST API's to invoke views.
Again if a stand-alone server is possible maybe even message queues would work.
Maybe a cache like memcached can act as mediator. But then if one is going this route, its better to use Django sessions as it hides a whole lot of implementation details.
Lastly, as an extension to point 6, instead of files store data in some persistent storage mechanism like mysql.
The simplest way is to use sessions. Just add the id to the session and redirect to the thank you view, you read the id value and query the db with that id.