im using the comment system, now, i would like to re-write the segment form the url comment and append a symbol #, i want to move the page seccion to the comment list exactly to the last comment user with <a name=#{{comment.id}}?> username </a>
Im using next for redirect the usen when the comment was posted:
{% get_comment_form for object as form %}
<form action="{% comment_form_target %}" method="POST">
{{ form }}
<input type="hidden" name="next" value="{{ object.get_absolute_url }}" />
<input type="submit" name="preview" class="submit-post" value="Preview"></td>
</form>
But in the Django Doc dont say nothing about rewrite or customizer the comment redirect / url
Any idea?
Thanks
I just stumbled across this little bit of ugliness. After reading the source code I didn't see any nice way to override this behavior. By default you are redirected to the URL in the template's {{ next }} variable and Django appends a ?c=1 to the URL where the 1 is the ID of the comment. I wanted this to instead be #c1 so the user is jumped down the page to the comment they just posted. I did this with a little bit of "monkey patching" as follows:
from django.contrib.comments.views import utils
from django.core import urlresolvers
from django.http import HttpResponseRedirect
def next_redirect(data, default, default_view, **get_kwargs):
next = data.get("next", default)
if next is None:
next = urlresolvers.reverse(default_view)
if get_kwargs:
next += '#c%d' % (get_kwargs['c'],)
return HttpResponseRedirect(next)
# Monkey patch
utils.next_redirect = next_redirect
Related
I have created views in Django that use LoginRequiredMixin. However, whenever I log in and am to be redirected to another url, the url I am redirected to ends with multiple slashes instead of the usual 1 slash at the end of a django url.
One of my views:
class BookListView(LoginRequiredMixin, ListView):
model = Book
# paginate enables the list view to fetch a certain number of records per page. This is
# useful when the records are plenty and it is not possible to display all in one page.
paginate_by = 3
My login.html template:
{%extends 'catalog/base_generic.html'%}
{%block content%}
{%if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{%if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.
</p>
{% else %}
<p>Please login to view this page.</p>
{% endif %}
{% endif %}
<form method="POST", action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{form.username.label_tag}}</td>
<td>{{form.username}}</td>
</tr>
<tr>
<td>{{form.password.label_tag}}</td>
<td>{{form.password}}</td>
</tr>
</table>
<input type="submit" value="Login"/>
<input type="hidden" name="next" value={{next}}/>
</form>
<P>
Forgot Password?
</P>
{% endblock %}
When I am just logging in, all seems Okay:
Login Page
However, after login, this happens:
Error Message with multiple slashes at the end
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/catalog/books///
Using the URLconf defined in locallibrary.urls, Django tried these URL patterns, in this order:
admin/
catalog/ [name='index']
catalog/ books/ [name='books']
catalog/ book/<int:pk>/ [name='book-detail']
catalog/ authors/ [name='authors']
catalog/ author/<int:pk>/ [name='author-detail']
accounts/
^static/(?P<path>.*)$
The current path, catalog/books///, didn’t match any of these.
You’re seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
I can't figure out what I am doing wrong. Please help!
you forgot some quotes around {{ next }}
change it to
<input type="hidden" name="next" value="{{next}}"/>
#settings.py
APPEND_SLASH: bool = True # by default
Setting it to False will resolve your extra slash issue. Check the documentation; there are some drawbacks if set to False in production.
Make Use of Doctests Module to debug what's causing the issue.
>>> from django.test import SimpleTestCase
>>> from django.test import Client
>>>
>>>
>>> class TestRedirect(SimpleTestCase):
... """There is a good chance it will return redirect and
... and return 301 response; if APPEND_SLASH is causing the issue."""
>>>
>>>
>>> def test_catalog_book(self):
... client = Client()
... response = client.get("/catalog/book/")
... self.assertEqual(response.status_code, 301)
Read the documentation of advanced testings
>>> response = c.get('/redirect_me/', follow=True)
>>> response.redirect_chain
[('http://testserver/next/', 302), ('http://testserver/final/', 302)]
TLDR: <input type="hidden" name="next" value="{{ next }}"> is the line of code I don't understand. With it this system of redirecting works fine, however, I do not understand what it is doing. It's as if is linking the user to the next page but no link is actually clicked?
I understand that if a user is not logged in the next parameter is used to redirect the user to whatever #login_required decorated view they were trying to access after logging in. Though the way this happens seems a bit automagical.
I have the following login related settings:
LOGIN_REDIRECT_URL = 'dashboard' # tells django which url to redirect after login if no 'next' parameter is present in the request
LOGIN_URL = 'login' # url to redirect the user to log in (for example, views using the login_required decorator)
and am using the authentication views fount in django.contrib.auth (only included three for simplicity):
from django.urls import path
from . import views
from django.contrib.auth import views as auth_views
urlpatterns = [
path('', views.dashboard, name = 'dashboard'),
path('login/', auth_views.LoginView.as_view(), name = 'login'),
path('password_change/', auth_views.PasswordChangeView.as_view(), name = 'password_change'),
]
Here is the custom login.html tempate located at account/registration/index.html:
<div class="login-form">
<form action="{% url 'login' %}" method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<input type="submit" name="" value="Log-in">
</form>
</div>
Now say I am not logged in and try to access /account/password_change, I will be redirected to the login view and the path will be http://127.0.0.1:8000/account/login/?next=/account/password_change/, after logging in I can change my password with no problem. However, If I remove <input type="hidden" name="next" value="{{ next }}"> from index.html:
<div class="login-form">
<form action="{% url 'login' %}" method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" name="" value="Log-in">
</form>
</div>
and again try to access /account/change_password (as a non logged in user), I will be redirected to the login page and the url will be the same as before http://127.0.0.1:8000/account/login/?next=/account/password_change/. However, when I login this time I am redirected to the dashboard view (this makes sense to me, I defined this with LOGIN_REDIRECT_URL = 'dashboard' and did not provide a next parameter.
With all that said, what I don't understand is why after removing <input type="hidden" name="next" value="{{ next }}"> is the path at the login view sill http://127.0.0.1:8000/account/login/?next=/account/password_change/ (even though I will be redirect to dashboard and not password_change)? And why after adding <input type="hidden" name="next" value="{{ next }}"> back to the html does the browser know how to redirect the user to the correct page?
If anyone takes the time to read and respond to this, thanks in advance!
Nice question.
Please read this code segment
https://github.com/django/django/blob/master/django/contrib/auth/views.py#L71
If you observe closely, this line of code is trying to get data from POST as name next. If there is no key named next, it tries to get value from query parameter; I mean GET.
So, if we remove input from form (<input type="hidden" name="next" value="{{ next }}">), it still works as query params are acting as fallback.
Hope, it helps.
I've struggled with this problem for the last two days and could use some help. The home page for my Django 1.6 application will include two forms, one that a user can use to sign in to the site and one they can use to sign up (create a login) for the site:
# templates/home/home_page.html
<div class="sign-in-form">
<form action="{% url 'apps.home.views.sign_in' %}" method="post">
{% csrf_token %}
{{ sign_in_form.as_p }}
{% if next %}
<input type="hidden" name="next" value="{{ next }}">
{% else %}
<input type="hidden" name="next" value="{% url 'view-members' %}">
{% endif %}
<input type="submit" value="Sign in">
</form>
</div>
<div class="sign-up-form">
<fieldset>
<legend>Sign up</legend>
<form action="{% url 'apps.home.views.sign_up' %}" method="post">
{% csrf_token %}
{{ sign_up_form.as_p}}
<p><input type="submit" value="Sign up" /></p>
</form>
</fieldset>
</div>
If the user submits, the sign_in form, they'll be taken to a page where they can view other site members. If they submit the sign_up form, they'll be taken to a second signup page where they'll create a user profile, etc.
Originally, I was going to use the technique shown in this question and use one view to handle the homepage. However, I decided to try to use two views because I'm using the Django's actual login view (django.contrib.auth.views.login) so that I can add code to it to detect the user's device (phone, tablet, or computer), and merging that view with my sign_up view would create a very long and complicated view to maintain. I'd prefer to keep the views for both forms separate.
Here's the home page and sign_in views:
# apps/home/views:
def home_page(request, template):
sign_in_form = SignInAuthenticationForm()
sign_up_form = CreateAccountForm()
return render(request, template, {"sign_in_form": sign_in_form,
"sign_up_form": sign_up_form})
#sensitive_post_parameters()
#csrf_protect
#never_cache
def sign_in(request,
template='home_page.html',
redirect_field_name=REDIRECT_FIELD_NAME,
# authentication_form=AuthenticationForm,
authentication_form=SignInAuthenticationForm,
current_app=None, extra_context=None):
# Do device detection here...
# django.contrib.auth.views code goes here...
return response
The signup view will just be your typical, function-based view for processing a form as described in the Django documentation.
What I'm struggling with is my URLconf files. Here's my main and "home" URLconf files:
# conf/urls.py
urlpatterns = patterns('',
url(r'^$', include('apps.home.urls')),
# Other url patterns...
)
# apps/home/urls.py
urlpatterns = patterns('apps.home.views',
url(r'^$',
'home_page',
{'template': 'home/home_page.html'},
name='home-page'),
url(r'^sign_in/$',
'sign_in',
{'template': 'home/home_page.html'},
name='sign-in'),
url(r'^sign_up/$',
'sign_up',
{'template': 'home/home_page.html'},
name='sign-up'),
)
The problem is that I get this error during template rendering:
NoReverseMatch at /
Reverse for 'apps.home.views.sign_in' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$sign_in/$']
Request Method: GET
Request URL: http://localhost:8000/
Django Version: 1.6.2
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'apps.home.views.sign_in' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$sign_in/$']
Exception Location: /Users/smith/venv/swing/lib/python2.7/site-packages/django/core/urlresolvers.py in _reverse_with_prefix, line 429
Python Executable: /Users/smith/venv/swing/bin/python
Python Version: 2.7.5
Python Path:
['/Users/smith/Dropbox/www/swing',
'/Users/smith/venv/swing/lib/python2.7/site-packages/wurfl_cloud-1.0.1-py2.7.egg',
'/Users/smith/venv/swing/lib/python27.zip',
'/Users/smith/venv/swing/lib/python2.7',
'/Users/smith/venv/swing/lib/python2.7/plat-darwin',
'/Users/smith/venv/swing/lib/python2.7/plat-mac',
'/Users/smith/venv/swing/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/smith/venv/swing/Extras/lib/python',
'/Users/smith/venv/swing/lib/python2.7/lib-tk',
'/Users/smith/venv/swing/lib/python2.7/lib-old',
'/Users/smith/venv/swing/lib/python2.7/lib-dynload',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/smith/venv/swing/lib/python2.7/site-packages']
At first I started to think that maybe it's telling me that it can's find the correct URL pattern in my home/urls.py file because the URL signature in my form is incorrect. Maybe I needed to do this to match the arguments in the sign_in view:
<form action="{% url 'apps.home.views.sign_in' 'home/home_page.html' %}" method="post">
But I'm already showing the template name in the home URLconf. And I don't think I need to pass the other view arguments in the form action (e.g. redirect_field_name) because their optional. In any case, adding this argument to the form action didn't fix it.
One of the things that confuses me is how to set the first url argument. I've set them to r'^sign_in/$' and r'^sign_up/$' because if I set them both to r'^$', the page will render properly but when I submit either form, it justs posts back to the home page. You can see this will happen by doing a "view source" on the page. It shows each form's action will be "/". On the other hand, the way I have it now seems incorrect to me because the site won't actually have a "/sign_in/" and "/sign_up/" URL since both forms are on the home page. Also, is there going to be a problem in which if the user submits one for or the other improperly, errors for both forms will be rendered on the page?
The Django documentation, to the best of my knowledge, doesn't really describe a standard approach for doing what I'm trying to do. It describes how to render multiple versions of the same form. Can anyone tell me what I'm doing wrong?
Thanks.
Your form names are 'sign_in_form' and 'sign_up_form', but in your html you wrote them 'form.as_p' instead of 'sign_in_form.as_p' and 'sign_up_form.as_p' this is the first bug a saw in your code.
The real problem is in your urls configuration. In your main urls.py you have
url(r'^$', include('apps.home.urls')),
Other ...
Though you will not be able to get to localhost:8000/sign_in/ because initially it does not satisfy to ^$ .
Try to change it by
url(r'', include('apps.home.urls')),
and put it to the end of urls.py.
i test this see if this what you want:
view.py
def loginUser(request,**Kargs):
LoginFormSet = formset_factory(LoginForm)
SignFormSet = formset_factory(SignForm)
if request.method == 'POST':
login_formset = LoginFormSet(request.POST, prefix='login')
sign_formset = SignFormSet(request.POST ,prefix='sign')
if login_formset.is_valid():
#do somthing
elif sign_formset.is_valid():
#do somthing
return render(request, 'reservetion/login.html',{'login_formset': login_formset,'sign_formset':sign_formset})
else:
login_formset = LoginFormSet(prefix='login')
sign_formset = SignFormSet(prefix='sign')
return render(request, 'reservetion/login.html',{'login_formset': login_formset,'sign_formset':sign_formset})
page.html:
<form action="{% url 'loginUser' %}" method="post">
{% csrf_token %}
{{ login_formset.management_form }}
{% for form in login_formset %}
{{ form }}
{% endfor %}
{{ sign_formset.management_form }}
{% for form in sign_formset %}
{{ form }}
{% endfor %}
I have made a blog with Django with articles (like so: mysite.com/a/article_id/) and would like users to be able to comment on the article's comment page (i.e: mysite.com/a/article_id/comments/)
So far I haven't had much success. It seems that the article_id in the url is blocking somehow the comments app.
This is my url.py:
from django.conf.urls import patterns, include, url
from django.contrib.auth.views import login, logout
urlpatterns = patterns('blogengine.views',
url(r'^$', 'get_posts', name='index'),
url(r'^write/', 'write_post', name='write'),
url(r'^a/(?P<post_id>\d+)/$', 'detail'),
url(r'^a/(?P<post_id>\d+)/comments/$', 'detail_comments'),
url(r'^a/(?P<post_id>\d+)/comments/', include('django.contrib.comments.urls')),
)
These are my views - views.py:
def detail_comments(request, post_id):
p = get_object_or_404(Post, pk=post_id)
return render_to_response('blogengine/detail_comments.html', {'post': p},
context_instance=RequestContext(request))
And this is my template detail_comments.html
{% block content %}
{% load comments %}
{% get_comment_form for post as form %}
<form action="/a/{{ post.id }}/comments/post/" method="post">
{% csrf_token %}
{{ form.content_type }}
{{ form.object_pk }}
{{ form.timestamp }}
{{ form.security_hash }}
<p style="display:none"><label for="id_honeypot">Leave blank</label>{{ form.honeypot }}</p>
<p>
<label for="id_comment">Comment</label>
{{ form.comment }}
</p>
<p><input type="submit" name="post" value="Post →" /></p>
</form>
{% endblock %}
(Oh and this is kind of obvious but the comments app is installed in settings.py)
If the form action is set to {% comment_form_target %}, like suggested in the docs, django throws this error:
NoReverseMatch at /a/2/comments/
Reverse for 'django.contrib.comments.views.comments.post_comment' with arguments '()' and keyword arguments '{}' not found.
I tried "hacking" my way out by replacing it with this /a/{{ post.id }}/comments/post/ which works to display the page but then if I try to post a comment, django throws a different error:
TypeError at /a/2/comments/post/
post_comment() got an unexpected keyword argument 'post_id'
Is there a way to get the comments app to ignore the id_post? Or another way to do this?
Thanks.
The error message is pretty unambiguous: django.contrib.comments.views.post_comment does not take a post_id argument, so it throws.
As the comments views do not need nor want the argument, why not just leave it out?
You should be able to modify the URL route not to capture the post_id at all (although at the cost of consistency) like so:
url(r'^a/(?:\d+)/comments/', include('django.contrib.comments.urls')),
or simply
url(r'^a/\d+/comments/', include('django.contrib.comments.urls')),
Note that there's really no point in having this kind of nesting at this point anyway if it's just going to be ignored, so you could simplify it to:
url(r'^comments/', include('django.contrib.comments.urls')),
Granted, this doesn't look as pretty and pseudo-RESTful without the vestigial prefix, but there's really no point in having it there if you're just going to ignore it.
The other thing you could do would be to wrap all the views the comments app provides so they throw a 404 if the post_id is invalid, but that seems overkill.
Ok so I solved my problem by simply doing what the docs say. I imported the comments like so:
url(r'^comments/', include('django.contrib.comments.urls')),
And kept this url pointing to my detail_comments view which displays the comment list and form:
url(r'^a/(?P<post_id>\d+)/comments/$', 'detail_comments'),
So basically the processing happens at /comments/ but the user interacts with this page: /a/post_id/comments/
The only problem I had was that the Django comments app automatically redirected the user to a success page after posting a comment.
I solved this by setting a "next" hidden field in the form indicating the current page.
I'm using django.contrib.comments for enabling comments on a blog.
I was adding a hidden 'next' field to the comment form with the url to which I'd like the user to return after they submit their comment in order to bypass the posted.html template, which was working properly:
<input name="next" type="hidden" value="{% url single_post slug=post.slug %}" />
However, after implementing the comment moderatar as follows:
from django.contrib.comments.moderation import CommentModerator, moderator
class PostModerator(CommentModerator):
email_notification = True
moderator.register(Post, PostModerator)
, there was an error complaining that the file comments/comment_notification_email.txt was missing, so I created the file as follows:
Comment: http://127.0.0.1{{ comment.get_absolute_url }}
From: {{ comment.person_name }}
-----
{{ comment.comment }}
-----
Admin: http://127.0.0.1/admin/comments/comment/{{comment.id}}/
But now, Django complains that the request URL http://127.0.0.1:8000/comments/post/ does not exist? How can this issue be best solved?
Doing a redirect in a separate view solved my issue:
urls.py
(r'^comments/post/', 'app.views.comment'),
views.py
def comment(request):
# Redirecting after comment submission
return HttpResponseRedirect(request.POST['next'])