Django URLs - make 'cleaner'? - python

Hi I have a django application, and I was wondering if there was a better way to display my URLS after submitting a GET request.
urlpatterns = [
...
re_path(r'^reporting/$', ReportView.as_view(), name='report'),
]
When I got to localhost:8000/reporting which displays a form, and click on a radio button and submit, it takes me to:
http://localhost:8000/reporting/?run=2&submit=Search+for+run
I would prefer it if it was something like:
http://localhost:8000/reporting/run=2/
because this page displays another form, which I would like to 'add on' to this:
http://localhost:8000/reporting/run=2/choice=primary/
Is this possible - would I have to have several different URLS relating to different views?

Rather than trying to mangle URLs like that (and I'd challenge the assertion that your way is "cleaner"), you could preserve the existing parameters by outputting hidden fields within the form which would then be sent along with the visible ones:
<form method="GET">
{% for key, value in request.GET.items %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
.. rest of form ..
</form>

#Daniel's answer is correct.
But the answer of your question
Is this possible - would I have to have several different URLS relating to different views?
is also YES.
Your ReportView is kind of ListView. I guess it shows all list from Report.
Then you can make DetailView from Report - which shows one report object per page.
It should be like
http://localhost:8000/reporting/2/
And instead of 2, you can add anything you want (Report's title, slug is find) - but it should be UNIQUE so I recommend pk(id) or slug.
For make DetailView, you have to make view (like ReportDetailView), add urls, and make templates for view.
You should check django detailview docs for more information.

Related

Sending POST data from inside a Django template 'for loop'

With this HTML:
...
{% for thing in things %}
<form method="post">
{% csrf_token %}
{{ thing.name }}
{{ form.value }}
<input type="submit" value="Submit" />
</form>
{% endfor %}
...
My website lists multiple 'things' from my database, so there can be many forms generated on the one page. How can I somehow determine in my views.py, which 'thing's' form is being submitted?
More elaboration:
Imagine you have a page of objects listed one after the other, and each object has a like button associated with it, that adds a like to the object it is next to. That's essentially what I'm trying to do here.
The problem is, I have a form that can process the like, but how do I take that like and add it to the object that it's displayed next to on the page? (by the aforementioned 'for loop')
I'm completely confused on how to go about this, am I looking at the problem the wrong way, or is there a standard idiom around this problem that I don't know about?
Thank you :)
The most common design pattern for model instance updates is to provide the primary key of an object in the url where you are submitting your post data.
# urls.py
from django.conf.urls import *
from library.views import UpdateThing
urlpatterns = patterns('',
url('^update_thing/(?P<pk>[\w-]+)$', UpdateThing.as_view(), name='update_thing'),
# views.py
def my_view(request, pk=None):
if pk:
object = thing.objects.get(pk=pk)
form = MyModelForm(data=request.POST or None, instance=object)
if form.is_valid():
...
Now, let's specify (using Django's url template tag) that we want to submit post data for each object to the correct url.
{% for thing in things %}
<form method="post" action="{% url 'update_thing' thing.pk %}">
{% csrf_token %}
{{ thing.name }}
{{ form.value }}
<input type="submit" value="Submit" />
</form>
{% endfor %}
The url tag does a reverse lookup through your urls for the name kwarg supplied for a given url, and accepting positional arguments (such as, in this case, thing.pk) and, when needed, keyword arguments.
The standard way to handle multiple forms of the same kind on one page with Django is to use Formsets.
It handles the annoying details like displaying errors on one form while preserving the input on others etc.
However, in your specific case that might be overkill. If you just want to create a like for an object, there isn't really any user input that needs to be validated, so you don't really need a form. Just perform a POST to a specified URL, maybe with Javascript. If the user messes with the URL, you display a 404.

How does Django's default template form rendering ( {{ form }} ) work?

The most basic way to render a form in a template in Django is to add an instance of the form to the context:
context['form'] = MyForm()
And then, in your template, render the form by calling it as a template variable:
<form method="POST">
{% csrf_token %}
{{ form }}
</form>
When this template is rendered, {{ form }} gets rendered as:
<label for="my_field">my_field:</label>
<input type="default_widget_for_field_type" name="my_field" />
with one pair for each field in your form.
My question is: what code path on the form and/or in the templating language actually does this rendering? Based on other conventions in the Django templating language, I had expected the Python underlying {{ form }} to be form(), much like {{ object.get_absolute_url }} ends up being object.get_absolute_url(), but when I instantiate an instance of my form in the shell and try to call it, I get a TypeError telling me it's not callable. I had a look at the Django source code trying to figure out where this gets done, but I didn't have much luck.
Here's my use case. I want to create a ModelForm for a model representing a comment on a piece of content. It has a foreign key to that content model. There are multiple pieces of content on each page, so I need to instantiate multiple, unbound copies of the ModelForm to create comments on each piece of content. Each form will be attached to its content object on the page, so I don't want the foreign key to be a user-editable field.
This means that I need to take care of populating the foreign key field, and it makes sense to me to do that from the template as a hidden form field, because the template will have access to the object that it needs to set the foreign key to. However, if I leave the foreign key field off of the ModelForm, then the model won't save correctly on form validation. I could override the form validation to add in the hidden field, but I feel like a more elegant way to do it would be to add the field to the ModelForm, then override the ModelForm's rendering method so it doesn't try to render that field. Then I'll add the field manually in my template.
{% for piece_of_content in page_contents %}
{# the content #}
<p>Add your comment!</p>
<form method="POST" action="{% url 'comment-create' %}">
{% csrf_token %}
{# the overridden rendering method won't render the foreign key field... #}
{{ form }}
{# then I add it manually #}
<input type="hidden" name="commented_on" value="{{ piece_of_content.id }}" />
</form>
Then I wire the url for 'comment-create' to a CreateView, set its form_class to the ModelForm, and it will do its thing without any additional input from my end.
If the form were just callable, like I thought, then I could override __call__ on the ModelForm, and tell it to render everything but the foreign key field. But the ModelForm doesn't have a __call__, and I can't figure out where it is done! The form has methods for rendering various other configurations (as_p, as_ul, and as_table), but none that I see for just the default rendering.
Including {{ form }} is the same as {{ form.as_table }}. This is because the form's __str__ method calls its as_table() method. See the docs on outputting forms as HTML for more info.
The docs on template variables and lookups explain how variables are included in the template. If attributes are callable, the method is called. However in your case, form instances are not callable, so the unicode representation of the form is displayed.
If you don't want users to edit a field, I suggest you exclude it from the form and set it in the view. Users can tamper with the values of hidden fields.
The answer to your question is that this is just the string representation of the form: __unicode__() calls as_p() which renders the form.
However, this really isn't the way to do it. Rather than trying to hack both output and validation, and then try to insert the value, you should just exclude the foreign key from the form altogether. Then simply add it when you save: you can get the relevant value from the URL that the form posts to, and add it there:
if form.is_valid():
obj = form.save(commit=False)
obj.content_id = value_from_url
obj.save()

How to post form within one page without use HttpResponseRedirect() in Django

I'm writing a simple blog application, I used HttpResponseRedirect() to redirect to the blog detail after users commented like this
return HttpResponseRedirect(reverse('detail', args=(in_blog_id)))
but when I click the back button in my browser, it would turn to the page before submit comment, I mean, the page have the comment form fulfilling, I just want users turn back to the blog list when they click back button after commenting.
So does it have a function or a way in Django to do it?
I'm sorry I didn't make it clear above, I mean I just want to post form within one page, I'm using a CommentView() and make a url() direct to this view,
<form action="/blogs/{{ blog.id }}/comment/" method="post">
{% csrf_token %}
<label>Name:</label><input type="text" name="author"/>
<label>Comment:</label><input type="text" name="comment_text" />
<input type="submit" value="Submit" />
use this code in my html file to make a form. When I click submit it would turn to /blogs/{{ blog.id }}/comment/ and then I use HttpResponseRedirect() to turn back.
You cannot change the browser history with Django...but you can with javascript. You should look into the javascript history api.
This is a nice guide from Mozilla.

Trouble shooting Django form/view/template and how they all work together

I'm currently trying to learn Django from the book and I read most of it and am now trying to write a webapp of my own. I just really can't figure out how they all interact and how they aren't fitting together.
My urls.py file reads like this...
from django.conf.urls.defaults import patterns, include, url
from users.views import homepageview, submitted, referral_create
urlpatterns = patterns('',
(r'^$', homepageview),
(r'^submitted/$', referral_create),
the views.py file looks like this...
# Create your views here.
from django.shortcuts import render_to_response
from django import forms
from datreferral.users.forms import ReferralCode
def homepageview(request):
now = 'your damn referral code'
return render_to_response('datreferraltemplate.html', {'now': now})
def referral_create(request):
if request.method == 'POST':
form = ReferralCode(request.POST)
if form.is_valid():
new_code = form.save()
return HttpResponseRedirect(reverse(contact_details, args=(new_contact.pk,)))
else:
form = ReferralCode()
The form.py file looks like
from django import forms
class ReferralCode(forms.Form):
referralcode = forms.CharField()
and the template looks like this...
{% extends "base.html" %}
{% block title %}
Enter your referral codes!
{% endblock %}
{% block content %}
<h1>Enter your code</h1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="" method="POST">
<p>Subject: <input type="text" name="subject" value="{{ referralcode }}"></p>
<input type="submit" value="Submit">
</form>
{% endblock %}
Hopefully that's enough info.
I am looking for two things. First off, when I try to view the page after submitting the form i get no where because I assume that the "if request.method == 'POST':" is not triggering. Obviously its something pretty obvious but I'm in one of those modes where I can find the bug for the life of me.
The second question I have is basically is plea for help. somehow after reading through those chapters multiple times I can't seem to nail down how all of the pieces interact with each other. I know that the template and the urls.py and the views.py interact and I get (I think) but I can't really grasp how the database and forms interact with each other and with the views/templates. like say I just wanted to a simple form where whatever the use inputted is written into the database... How would you do that? I'm using postgres if that matters.
Note: the form and the in the template is a modified version of code i found on here and tried to manipulate to meet my needs but failed so don't be overly thrown off if that doesn't make sense I wasn't able to mess with that part that much yet because of these problems.
As I'm new to web development I really appreciate any one willing to help me or point me in the right direction.
You don't return anything in you else clause. A views must always return a response, which can be pretty much anything, but in most cases you will return a instance of a (sub)-class of HttpResponse (I really like the render shortcut). It is a good idea to have a default return at the bottom of you view, add some early returns for "special" responses and otherwise let the execution reach the default return - this way you never have the case where you return nothing.
You have to use a Model to save data (have you work through the tutorial?). Usually the excution model is the following:
you app gets a request and the urls.py is searched for a view that should be called
the request is passed through the middleware
you view is called and "does" something. I.e. it fetches data from the database (again using a model) or stores something in the db (or saves a uploaded file, sends a mail, etc. - aview can really do anything)
you view returns "something"
the returned "something" is passed through the middleware and will eventually be tranformed in a stream of data that is passed to the browser
Thats it. This is a bit simplified but it pretty much covers all important parts.
Well, there are quite a few things wrong here.
First is that your form is being rendered by the base view, homepageview, and is submitting back to that same URL. However the form processing logic is in a separate view, which isn't being called. Either change the action parameter of the form to point to the other URL or - better - move all the logic into the same view.
Secondly, your form processing view isn't returning a response or rendering a template. That's an error in Django, and if you had managed to call that view you would see an exception.
Thirdly, I find it hard to believe you've read the entire Django book and not seen anything about models or databases. You've got no indication here that you've set up any models, but without them Django won't write anything to a database. You'll need to do that, then change your form into a ModelForm subclass, after which you can call form.save() successfully.

The "next" parameter, redirect, django.contrib.auth.login

I'm trying to redirect users to custom url "/gallery/(username)/" after successfully logging in. It currently redirects to the default "/account/profile/" url While I know what I can override the redirect url in my settings.py, my url is dynamic thus it will not work.
Documentation states that I need to use the "next" parameter and context processors. I have the {{next}} in my template, but I'm confused on how to actually pass the "/gallery/(username)". Any help would be greatly appreciated.
p.s: I'm trying to steer away from writing my own login view.
Django's login view django.contrib.auth.views.login accepts a dictionary named extra_context. The values in the dictionary are directly passed to the template. So you can use that to set the next parameter. Once that is done, you can set a hidden field with name next and value {{ next }} so that it gets rendered in the template.
I confess I usually use 2 redirects in order to get something like this to work.
First, Make your own registration/login.html page. You can copy-and-paste the html example in this section of the authentication docs to make the process a little easier. Instead of using the dynamic '{{ next }} variable from the context, however, hardwire the value of next to go to a generic landing view of logged-in users
<input type="submit" value="login" />
<input type="hidden" name="next" value="/gallery/" />
Then, in the view that you map to the /gallery/ URL, extract the User object from the request (since the user will now be logged in, especially if the gallery view is wrapped in a #permission_required or #login_required decorator. Use that view to redirect to the appropriate user-specific gallery page:
#login_required
def gallery(request):
url = '/gallery/%s/' % request.user.username
return HttpResponseRedirect(url)
If you already have the custom template for login form you need to add the following inside your <form> tag:
<input type="hidden" name="next" value="{{next}}" />
BTW, you don't have to create your own login view. django.contrib.auth.views.login works fine. You only need to create a template for it (registration/login.html)
being an newbie to django and stumbling over this somewhat older thread i found a differing solution for the problem of dynamically (=override a custom default only if needed) setting the next-param that i'd like to share (working fine with django 1.5, earlier versions untested):
just as django-d i wanted avoid repetition and a custom login-view, so i used the stock django.contrib.auth.views.login-view by adding the line of
url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html',}, name='login'),
to my urls.py and within the login.html-templates form-element:
{% if not next or not next.strip %}
{# avoid django.contrib.auth.views.login s default of /account/profile/ #}
{% url 'afterlogindefaultview' as next %}
{% endif %}
<input type="hidden" name="next" value="{{ next }}" />
which to my understanding follows the decoupling-practice of the url-configurations from the views.
so for views that should redirect to my apps login and afterwards head to a non-default view
i use
return HttpResponseRedirect('%s?next=%s' % (reverse('login'), reverse('mycustomnext')) )
from the view where i want to have the user to log in. i use this to get back to the view where i left off for logging the user in.
You can use a static redirect to /loggedin/ and then associate the url to a view that makes the correct redirect.
Login takes an extra step but if you want to use django's view it does the job.
create your own view for logging in, with it's own url, don't use the admin's one.
you can store the next page in the session, or pass it as a GET parameter to the login view
(i.e. /login?next=gallery) just don't forget to sanitize and validate that value before redirecting to it.

Categories