I am new to Django and am using Django's user auth package (django.contrib.auth) for user login, password reset, etc.
Now, while everything works just fine, on the logon form, I'd like to use the html-placeholder property. How can I use / populate this? I did find some answers (e.g. this one) but I do not understand where to place this code, how to extend the view / form or even the model (e.g. adding new fields) as this gets delivered with the standard package.
Right now, I have added the following:
forms.py
from django import forms
from django.contrib.auth.forms import AuthenticationForm
class LoginForm(forms.Form):
username = forms.CharField(label='username')
password = forms.CharField(label='password')
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs['placeholder'] = 'Username'
self.fields['password '].widget.attrs['placeholder'] = 'Password'
I am not sure what I need to do in urls.py or models.py or anywhere else for the code to be executed.
I found the following solution:
installed bootstrap4
using the following tag in my html:
{% bootstrap_field form.password field_class="field" placeholder="Password" show_label=False %}
I believe by adding the widgets to your forms init, when you instantiate the form in your template the placeholder should appear like intended. Did you just try rendering your form such as {{form}} in the template and see if it rendered it? I dont think bootstrap4 is necessary but it is a great tool.
Related
I long searched the internet and this site looking for a solution but nothing work. The problem is that in any registration form I make in Django, each time a user click on a username or password field, a list of previously entered values is shown and this behaviour is not desirable.
The raw fact is that, if I directly comment this line
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
in
# UserCreationForm, auth/form
password1 = forms.CharField(
label=_("Password"),
strip=False,
#widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
help_text=password_validation.password_validators_help_text_html(),
)
everything work as I want including the username.
But I don't want to touch the Django UserCreationForm in auth/forms. The ideal would be subclass it, and customize it. Here what I did
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
# prevents the form to automatically autofocus
self.fields['email'].widget.attrs.pop("autofocus")
self.fields['username'].widget.attrs.pop("autocomplete")
#self.fields['password1'].widget.attrs.pop("autocomplete")
#self.fields['password1'].widget.attrs.update({'autocomplete':'off', 'maxlength':'32'})
...
I tried it with many combinations, including with new-password, off, None, or even empty strings, everything is ignored.
I repeat, the only way to not autocomplete the username and password fields is to comment the widget line in the original class, but this would be a very bad idea and, especially, it would break every time I will upgrade Django.
Any other reasonable solutions?
I once had a registration form I wanted to get this same behaviour with... What worked for me was, for example:
forms.py
class RegistrationForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={..., 'autocomplete': 'new-password'}))
email= forms.EmailField(widget=forms.EmailInput(attrs={..., 'autocomplete': 'new-password'}))
password = forms.CharField(widget=forms.PasswordInput(attrs={..., 'autocomplete': 'new-password'}))
# Setting the autocomplete='off' from within the form class by using {'autocomplete': 'new-password'} to remove the suggested list over/below the input field
In your case, probably you could try...
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update({'autocomplete': 'new-password'})
self.fields['email'].widget.attrs.update({'autocomplete': 'new-password'})
self.fields['password1'].widget.attrs.update({'autocomplete': 'new-password'})
Also note that you'd have to add autocomplete="off" on your form tag as well.
<form method="POST" autocomplete="off">
...
...
</form>
Disclaimer:
While the username and email fields aren't displaying the suggested list anymore, the password field continues for many persons. In my case it did, so as a work around I changed up some stuff to the following...
# Instead of a password field, I've changed it to a text field
# Setting onkeyup event that will replace the text to asterisks
password = forms.CharField(widget=forms.TextInput(attrs={'id': 'password', 'autocomplete': 'new-password', 'onkeyup': 'hideCharacters()'}))
Within the html file:
<form method="POST" autocomplete="off">
...
...
</form>
# This script can be within a javascript file or the html: up to you...
# Script to replace text from field to asterisk(s).
<script type="text/javascript">
function hideCharacters() {
var x = document.getElementById("password");
x.value = x.value.replace(/./g, "*");
}
</script>
In a nutshell, that did the job for me.
I'm using the built in django admin site for the application's admin site in a sense that POST requests are built identically to its equivalent admin site, and sent to the same urls. So it's like the application admin site is just a wrapper for the admin site.
My problem is, I don't know how to redirect back to url where it was originally posted. Currently it redirects to the Change User admin page. I'd like it to redirect back to the page.
Here's my change-password-template.html:
<div>
<form action="{% url 'admin:auth_user_password_change' user.pk %}" method="post" id="change-password-form">
{% csrf_token %}
# ... here goes the password fields
<input type="submit" value="Change password">
</div>
<input type="hidden" name="next" value="{% url 'change_password' id=user.pk%}"> # my attempt at trying to redirect back to the original webpage
</div>
</form>
</div>
So, it does correctly post to admin/auth/user/{{user.pk}}/password/ but instead of redirecting back to the that page it instead redirects to: admin/auth/user/{{user.pk}}/change/. How can I make it do so?
The wrong way to do it...
You would need to overwrite the user_change_password method in your User admin, since that is where the redirect happens:
# contrib.auth.admin.py
def user_change_password(self, request, id, form_url=''):
...
if request.method == 'POST':
form = self.change_password_form(user, request.POST)
if form.is_valid():
...
return HttpResponseRedirect(
reverse(
'%s:%s_%s_change' % (
self.admin_site.name,
user._meta.app_label,
user._meta.model_name,
),
args=(user.pk,),
)
)
Since this ModelAdmin is registered in the auth app, you will need to unregister that, add your own custom UserAdmin. There are clear instructions on how to do this in the django docs. Once you have done that simply overwrite the method above and edit the redirect.
This would be much more effort than it's worth however, and more importantly it would also be a nasty hack. It's not what the admin interface is intended for. (It would also have the unintended side-effect that when you actually do use the admin app, it will now redirect to wherever you now have told it to - this would be very unusual behaviour).
A better solution...
If you want to add an end-point where a user can change their password it's not difficult to do it yourself, and that would be the best solution in this case.
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.urls import reverse
def change_password(request, user_id):
new_password = request.POST["password"]
user = User.objects.get(id=user_id)
user.set_password(new_password)
user.save()
return HttpResponseRedirect(reverse('your_url_here))
You'll need to add some additional checks (e.g. make sure the user has the appropriate permissions, make sure the request is a POST etc.).
Probably better still...
Django actually has a built in view for doing this, that you can subclass to customise.
# views.py
from django.contrib.auth.views import PasswordChangeView
class MyPasswordChangeView(PasswordChangeView):
success_url = reverse("url_to_redirect_to")
# urls.py
urlpatters = [
path('change_password', MyPasswordChangeViews.as_view())
]
You can read more about these built in views here.
Level: Absolute Beginner, trying to build an app to perform some db operation through web UI
models.py
from django.db import models
class MysqlUser(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(max_length=50)
environment = models.CharField(max_length=50)
forms.py
from django import forms
from onboard_app.models import MysqlUser
class MysqlUserForm(forms.ModelForm):
CHOICES = (
('test', 'Test'),
('develop', 'Develop'),
('staging', 'Staging'),
)
environment = forms.MultipleChoiceField(choices=CHOICES)
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = MysqlUser
fields = ('username', 'password', 'environment')
views.py
from django.shortcuts import render
from onboard_app.serializers import MysqlUserSerializer
from rest_framework import generics
from onboard_app.forms import MysqlUserForm
from onboard_app.models import MysqlUser
from django.views.generic.edit import CreateView, UpdateView, DeleteView
class MysqlCreateView(CreateView):
model = MysqlUser
form_class = MysqlUserForm
template_name = 'mysqluser_form.html'
success_url = '/mysql/user/add/'
mysqluser_form.html
{% extends "myapp/base.html" %}
{% block title %}MyApp{% endblock %}
{% block content %}
<h1>MySQL User Access</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Grant Access">
</form>
{% endblock %}
I'm trying to get Value(s) of field or MultipleChoiceFiled environment after the user Form Submit, and loop through the entered values to perform some action. I have been trying this for long, still can't figure out how. I don't want to process anything in the frontend html. I'm thinking it has to be processed in the Views but not sure how to get the values of the field and loop over.
Any examples or any django concepts to look into will help me a lot. Any help is appreciated, thanks in advance!
Unless you have already done so, I recommend going through Django docs on class-based views (https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/) to get an overview of how the whole thing works.
So after you submit a form, a post() method of your view is called. CreateView provides a default implementation, which validates the user input using the MysqlUserForm you have provided and then creates an instance of MysqlUser and redirects to the success_url.
If you want to add more logic, you need to overwrite the post() method (or some other method, called by post(), in particular, the form_valid method) and put your logic there. To get a complete sense of how things work, I recommend you to read through the CreateView, BaseCreateView, ProcessFormView, and ModelFormMixin source code, although the inheritance looks a bit complicated (and it is). More to that, I really advise you to walk through the request processing from the View.dispatch method all way to the form_valid method using the debugger and see how things really work. Trust me, it will really contribute to your development skills improvement and understanding. Actually, I've discovered the form_valid method to write this answer by reading the source code (I use rest-framework nowadays and don't remember a lot about Django views).
So, what you need is
class MysqlCreateView(CreateView):
model = MysqlUser
form_class = MysqlUserForm
template_name = 'mysqluser_form.html'
success_url = '/mysql/user/add/'
def form_valid(self, form):
environment = form.cleaned_data['environment']
# insert your code here
return super().form_valid(form)
P.S. A good IDE like a PyCharm is really much much more convenient to read source code, jump to relevant parts and debug than any text editor and PDB debugger.
I receive this message:
CSRF token missing or incorrect.
In most forums, the tell you to get the {% csrf_token %} in the form, and i do have it.
Also i have in my settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.csrf",
"django.contrib.auth.context_processors.auth",
)
I am using jinja, which didn't seem to use CSRF, but then i installed django registration and i got lost, since, it seems to be using some other views, that i don't have access to so to say, they are not written by me, and i can't figure out where they are. "standard auth views" as they call them. So i am unable to add "RequestContext".
Any ideas what's going on and how I can get it going? thanx
You might have to rewrite the django-registration view manually. Looks like there's an issue with how Jinja likes to do things and how Django wants to configure template loaders..
To look at the standard auth views, just look under "site-packages" in your python installation.
You could try wrapping the standard auth views like this:
from django.contrib.auth.views import login, logout
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def my_wrapped_login_view(request):
return login(request)
#csrf_protect
def my_wrapped_logout_view(request):
return logout(request)
I basically imported Django's standard auth views and called them with my own, which have the csrf_protect decoration. It's worth a shot.
Have you also got the standard Django Templating system installed? That will be required for most apps that are distributed with templates.
For CSRF, a context processor inserts the variable 'csrf_token' into the response context that it retrieves from the middleware if enabled. Now all you have to do, is make sure that it's apart of your form.
This is straight out of django.core, and is subject to change at any time.
if csrf_token:
if csrf_token == 'NOTPROVIDED':
return mark_safe(u"")
else:
return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % csrf_token)
However, seeing that, all you really need to know is that you have to have an input type named csrfmiddlewaretoken with the value of context.get('csrf_token','') within your form and that's all she wrote.
This answer isn't specific to django-registration, but just using Django with Jinja2 in general.
Django's CsrfViewMiddleware sets the csrf_token cookie if it determines that you have accessed the csrf_token context member. Unfortunately, Jinja2 rendering doesn't occur until after Django's middleware executes. As a result, the cookie doesn't get set, and therefore does not match the form, and you will get the 403 error.
To get around this issue, you need to access context['csrf_token'] at some point before you finish processing the response.
If you're using class-based views, you can create a CsrfProtectMixin:
class CsrfProtectMixin(object):
def render_to_response(self, context, **response_kwargs):
# Csrf processing happens when using a RequestContext.
# Be sure to use one.
if not isinstance(context, RequestContext):
context = RequestContext(self.request, context)
# Force access to csrf_token due to the way jinja2 renders the template
# after middleware has finished processing. Otherwise, the csrf cookie
# will not be set.
str(context.get('csrf_token'))
return super(CsrfProtectMixin, self).render_to_response(context, **response_kwargs)
And then in your view class:
class MyView(CsrfProtectMixin, TemplateView):
def get(self, request, *args, **kwargs):
context = {}
return self.render_to_response(context)
If you're not using class-based views, you can do something like:
def my_view(request):
context = RequestContext(request)
str(context['csrf_token']) #force access to the csrf_token
return render_to_response('template.html', context)
Or perhaps monkey patch render_to_reponse with the logic in the class, above.
The most straightforward answer to this one is to just put {% csrf_token %} within the form tag in your template/html.
I just switched off the csrf middleware in the settings like so, and now it works:
#'django.middleware.csrf.CsrfViewMiddleware',
I'm tooling around with Django and I'm wondering if there is a simple way to create a "back" link to the previous page using the template system.
I figure that in the worst case I can get this information from the request object in the view function, and pass it along to the template rendering method, but I'm hoping I can avoid all this boilerplate code somehow.
I've checked the Django template docs and I haven't seen anything that mentions this explicitly.
Actually it's go(-1).
<input type=button value="Previous Page" onClick="javascript:history.go(-1);">
This solution worked out for me:
Go back
But that's previously adding 'django.core.context_processors.request', to TEMPLATE_CONTEXT_PROCESSORS in your project's settings.
Back
Here |escape is used to get out of the " "string.
Well you can enable:
'django.core.context_processors.request',
in your settings.TEMPLATE_CONTEXT_PROCESSORS block and hook out the referrer but that's a bit nauseating and could break all over the place.
Most places where you'd want this (eg the edit post page on SO) you have a real object to hook on to (in that example, the post) so you can easily work out what the proper previous page should be.
You can always use the client side option which is very simple:
Back
For RESTful links where "Back" usually means going one level higher:
<input type="button" value="Back" class="btn btn-primary" />
All Javascript solutions mentioned here as well as the request.META.HTTP_REFERER solution sometimes work, but both break in the same scenario (and maybe others, too).
I usually have a Cancel button under a form that creates or changes an object. If the user submits the form once and server side validation fails, the user is presented the form again, containing the wrong data. Guess what, request.META.HTTP_REFERER now points to the URL that displays the form. You can press Cancel a thousand times and will never get back to where the initial edit/create link was.
The only solid solution I can think of is a bit involved, but works for me. If someone knows of a simpler solution, I'd be happy to hear from it. :-)
The 'trick' is to pass the initial HTTP_REFERER into the form and use it from there. So when the form gets POSTed to, it passes the correct, initial referer along.
Here is how I do it:
I created a mixin class for forms that does most of the work:
from django import forms
from django.utils.http import url_has_allowed_host_and_scheme
class FormCancelLinkMixin(forms.Form):
""" Mixin class that provides a proper Cancel button link. """
cancel_link = forms.fields.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
"""
Override to pop 'request' from kwargs.
"""
self.request = kwargs.pop("request")
initial = kwargs.pop("initial", {})
# set initial value of 'cancel_link' to the referer
initial["cancel_link"] = self.request.META.get("HTTP_REFERER", "")
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
def get_cancel_link(self):
"""
Return correct URL for cancelling the form.
If the form has been submitted, the HTTP_REFERER in request.meta points to
the view that handles the form, not the view the user initially came from.
In this case, we use the value of the 'cancel_link' field.
Returns:
A safe URL to go back to, should the user cancel the form.
"""
if self.is_bound:
url = self.cleaned_data["cancel_link"]
# prevent open redirects
if url_has_allowed_host_and_scheme(url, self.request.get_host()):
return url
# fallback to current referer, then root URL
return self.request.META.get("HTTP_REFERER", "/")
The form that is used to edit/create the object (usually a ModelForm subclass) might look like this:
class SomeModelForm(FormCancelLinkMixin, forms.ModelForm):
""" Form for creating some model instance. """
class Meta:
model = ModelClass
# ...
The view must pass the current request to the form. For class based views, you can override get_form_kwargs():
class SomeModelCreateView(CreateView):
model = SomeModelClass
form_class = SomeModelForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
In the template that displays the form:
<form method="post">
{% csrf token %}
{{ form }}
<input type="submit" value="Save">
Cancel
</form>
For a 'back' button in change forms for Django admin what I end up doing is a custom template filter to parse and decode the 'preserved_filters' variable in the template. I placed the following on a customized templates/admin/submit_line.html file:
<a href="../{% if original}../{% endif %}?{{ preserved_filters | decode_filter }}">
{% trans "Back" %}
</a>
And then created a custom template filter:
from urllib.parse import unquote
from django import template
def decode_filter(variable):
if variable.startswith('_changelist_filters='):
return unquote(variable[20:])
return variable
register = template.Library()
register.filter('decode_filter', decode_filter)
Using client side solution would be the proper solution.
Cancel