django: unable to save new user - python

I'm trying to build a view that shows a form for creating a new user.
Next are the template I'm using, urls.py and the code attempts at views.py and forms.py.
user_form.html
<form action="{% url 'app:register' %}" method="post">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Register">
</form>
urls.py
urlpatterns = [
...
path('newuser/',views.NewUser.as_view(), name='newuser'),
...
]
First attempt:
views.py
class NewUser(generic.CreateView):
model = User
fields = ['username','email','password']
template_name = 'app/user_form.html'
success_url = reverse_lazy('register')
This one didn't return errors, but was unable to save the new user data.
Second try involved creating a form for the occasion:
forms.py
class NewUserForm(forms.Form):
username = forms.CharField(max_length=100)
email = forms.EmailField()
password = forms.PasswordInput()
views.py
class NewUser(generic.CreateView):
model = User
form_class = NewUserForm
template_name = 'app/user_form.html'
success_url = reverse_lazy('register')
This one returned an error: TypeError at /newuser/ BaseForm.__init__() got an unexpected keyword argument 'instance'
Third:
forms.py
class NewUserForm(forms.ModelForm):
class Meta:
model = User
fields = ['username','email','password']
views.py
class NewUser(generic.CreateView):
model = User
form_class = NewUserForm
template_name = 'app/user_form.html'
success_url = reverse_lazy('cadastro')
Can't save a new User instance either.
I've also noticed that the model = User line in this last try can be removed.
I'm using the User.objects.all() query in the terminal and the admin page to check for new users.
What am I not doing?

You should make the POST request to the newuser view, so:
<form action="{% url 'app:newuser' %}" method="post">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Register">
</form>
You can however not use a simple ModelForm: passwords in Django are hashed and the ModelForm will not do that, or at least not automatically.
You can make use of the UserCreationForm [Django-doc] to do the hashing properly, this field also uses two password fields which will be validated.
If you want to implement a custom ModelForm, then you will need to implement the password hashing functionality in the model form:
class NewUserForm(forms.ModelForm):
class Meta:
model = User
fields = ['username','email','password']
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password'])
if commit:
user.save()
return user
and then plug this into the CreateView with:
class NewUserView(generic.CreateView):
model = User
form_class = NewUserForm
template_name = 'app/user_form.html'
success_url = reverse_lazy('cadastro')
Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names.
Therefore you might consider renaming the view class to NewUserView, instead of NewUser.

when you use CreateView on a model, it calls create method.
but User model is different. User model doesn't have create, instead have create_user. and CreateView doesn't know about it.
i suggest that you use View class like this:
class UserRegisterView(View):
form_class = UserRegistrationForm
template_name = 'account/register.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('home:home')
return super().dispatch(request, *args, **kwargs)
def get(self, request):
form = self.form_class()
return render(request, self.template_name, {'form':form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
cd = form.cleaned_data
User.objects.create_user(cd['username'], cd['email'], cd['password1'])
messages.success(request, 'you registered successfully', 'success')
return redirect('home:home')
return render(request, self.template_name, {'form':form})
this snippet totally works.

Related

Django Validation is working on django admin but not working on html template

I'm creating a form where if we register it should save data to the database if the form is valid. otherwise, it should raise an error but it doesn't save data to the database, and also some fields are required but if I submit the form it doesn't even raise the error field is required. but if I register it manually on Django admin pannel it works perfectly fine.
here is my model:
class foodlancer(models.Model):
Your_Name = models.CharField(max_length=50)
Kitchen_Name = models.CharField(max_length=50)
Email_Address = models.EmailField(max_length=50)
Street_Address = models.CharField(max_length=50)
City = models.CharField(max_length=5)
phone = PhoneNumberField(null=False, blank=False, unique=True)
def __str__(self):
return f'{self.Your_Name}'
also, I disabled html5 validation
forms.py
class FoodlancerRegistration(forms.ModelForm):
phone = forms.CharField(widget=PhoneNumberPrefixWidget(initial="US"))
class Meta:
model = foodlancer
fields = "__all__"
views.py:
def apply_foodlancer(request):
form = FoodlancerRegistration()
return render(request, 'appy_foodlancer.html', {"form": form})
and finally Django template
<form method="POST" novalidate>
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="cta-btn cta-btn-primary">Submit</button>
</form>
Thank you for your time/help
You don't have any form saving logic in your view.
Try something like this:
def apply_foodlancer(request):
if request.method == 'POST':
form = FoodlancerRegistration(data=request.POST)
if form.is_valid(): # if it's not valid, error messages are shown in the form
form.save()
# redirect to some successpage or so
return HttpResponse("<h1>Success!</h1>")
else:
# make sure to present a new form when called with GET
form = FoodlancerRegistration()
return render(request, 'appy_foodlancer.html', {"form": form})
Also check that the method of your form in your HTML file is post. I'm not sure if POST also works.
Avoid defining fields in a modelform with __all__. It's less secure, as written in the docs

Combining two separate forms in one Django view?

This question have been answered before, e.g here: Proper way to handle multiple forms on one page in Django
So before it gets marked as a duplicate. I'll try to explain why its different.
I've got three tables, Project, ProjectUser and User. ProjectUser is a join table to indicate what users belongs to what project.
I'm trying to create a view that lets users update project details (e.g. name of project), and also add users to the project (which is indicated by a dropdown that shows all available users like the standard one for models with foreign keys in the django admin panel). All works fine until I'm trying to pass an id from the views to the formclass and submit.
views.py
class ProjectUpdateView(UpdateView):
form_class = ProjectUpdateForm
second_form_class = ProjectUserAddForm
template_name = 'projects/project_edit.html'
success_url = reverse_lazy('projects:list')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
id_ = self.kwargs.get("id")
project = Project.objects.get(id=id_)
if 'form' not in context:
context['form'] = self.form_class()
if 'form2' not in context:
team = Organization.objects.get(id=project.organization_id)
context['form2'] = self.second_form_class(queryset=team) # <-- here is where I wish to pass a queryset, which fails when trying to submit form2.
context['project_users'] = ProjectUser.objects.filter(project__id=project.id).select_related("project")
context['team'] = Organization.objects.get(id=project.organization_id)
return context
def get_object(self):
id_ = self.kwargs.get("id")
return get_object_or_404(Project, id=id_)
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def form_valid(self, form):
project_id = self.kwargs.get("id")
if self.request.POST.get("form2") == 'Add':
ProjectUser.objects.create(user_id=self.request.POST.get("user"), project_id=project_id)
form.save()
success_url = reverse("projects:edit", args=(project_id,))
return HttpResponseRedirect(success_url)
def post(self, request, *args, **kwargs):
# get the user instance
self.object = self.get_object()
# determine which form is being submitted
# uses the name of the form's submit button
if 'form' in request.POST:
# get the primary form
form_class = self.get_form_class()
form_name = 'form'
else:
# get the secondary form
form_class = self.second_form_class
form_name = 'form2'
# get the form
form = self.get_form(form_class)
# validate
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})
projects_edit.html
<form action="{% url 'projects:edit' project.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.name|as_crispy_field}}
<input name="form" value="Update" type="submit"></input>
</form>
<form action="{% url 'projects:edit' project.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form2.user}}
<input name="form2" value="Add" type="submit"></input>
</form>
forms.py
class ProjectUpdateForm(ModelForm):
class Meta:
model = Project
fields = ["name"]
class ProjectUserAddForm(ModelForm):
def __init__(self, queryset, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['user'].queryset = User.objects.filter(organizations_organizationuser__organization__id=queryset.id) # here is where I wish to pass the id of the queryset from the form class
class Meta:
model = ProjectUser
fields = ["user"]
Rendering the forms works just fine with the desired queryset, but when I try to submit the second form (adding a user to the ProjectUserForm, I just get a
__init__() missing 1 required positional argument: 'queryset' error.
Any ideas on how to solve this? Perhaps I'm making it way more complicated than it should
I have also added a screenshot if it helps: https://imgur.com/a/uqu0UeB

Django, Modelform not rendering

I created a basic Modelform in django but my form does not render.
tasks.html
<table class="...">
<tr>
<th>Function</th>
</tr>
<tr>
<td>
<form method="POST">
{% csrf_token %}
{{ form }}
<button type="submit">Execute</button>
</form>
</td>
</tr>
</table>
models.py
class Tasks(models.Model):
#Task module
function_name = models.CharField(max_length=30, default='')
script_location = models.CharField(max_length=300, default='')
forms.py
from django.forms import ModelForm
from .models import Tasks
class Tasks(ModelForm):
class Meta:
model = Tasks
fields = ['function_name','script_location']
views.py
class Tasks(View):
def get(self, request):
form = Tasks()
return render(request, '.../tasks.html', {'form': form})
I except to see two text fields, but i only see the 'Execute' button
Short answer: avoid name clashes. Add suffixes to your form and view.
You named the model Tasks, the form Tasks and the view Tasks. Here the Tasks() call in your view will resolve to that view, so indeed:
class Tasks(View):
def get(self, request):
form = Tasks()
return render(request, '.../tasks.html', {'form': form})
Here you thus simply create a new Tasks object (the one that is a subclass of View).
I strongly recommend to rename your classes:
a model usually has no Model suffix, so you can keep this Task (singular);
a form can use a Form suffix, so TaskForm; and
your view can be renamed to TaskView.
After renaming, you thus can rewrite your view to:
class TaskView(View):
def get(self, request):
form = TaskForm()
return render(request, '.../tasks.html', {'form': form})
Here however, it looks that you basically implement a CreateView [Django-doc], like:
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
class TaskView(CreateView):
model = Task
fields = ('function_name','script_location')
template_name = '.../tasks.html'
success_url = reverse_lazy('certain-view')
This will for a GET request render the template with a form in the context. It will impelement a POST request as well that will create a model object and save it to the database, and then make a redirection to the certain-view. So it will implement a lot of boilerplate code for you.
change your forms.py
from django.forms import ModelForm
from .models import Tasks
class TasksForms(ModelForm):
class Meta:
model = Tasks
fields = ('function_name','script_location',)
add url in your apps urls.py
path('your_url/', views.TaskView, name='task'),
change your views.py
def TaskView(request) :
def get(self, request):
form = TasksForms()
return render(request, '.../tasks.html', {'form': form})
don't forget to add action of your form in tasks.html

login form keeps telling my username already exists django

So I am trying to make a login form for a django website using a forms.py file to generate a form in my html. I have already made a registration form using the same methods and that works fine but for some reason my login form keeps working like a registration form and saying that my username already exists. i assume it is just a litle mistake but I can't figure out what it is exactly.
forms.py
from django.contrib.auth.models import User
from django import forms
class LoginForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username', 'password']
models.py
class LoginFormView(View):
form_class = LoginForm
template_name = 'intranet/login_form.html'
def get(self, request):
form = self.form_class(None)
return render(request,self.template_name,{'form': form})
def post(self,request):
form = self.form_class(request.POST)
if form.is_valid():
username = request.POST.get['username']
password = request.POST.get['password']
user = authenticate(username=username,password=password)
if user is not None:
if user.is_active:
login(request,user)
return redirect('werknemers_list')
return render(request, self.template_name, {'form': form})
login_form.html
<h1>Log in</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="log in" />
</form>
urls.py
from django.conf.urls import url
from intranet import views
urlpatterns = [
url(r'^$', views.LoginFormView.as_view(), name='login'),
url(r'^registreer$', views.UserFormView.as_view(), name='registreer')]
You shouldn't use a ModelForm for this. That will always call the model validation, which in this case checks for username uniqueness.
Use a standard Form class instead.

How to save inline formset user field in Django using views

I've been using this great post http://kevindias.com/writing/django-class-based-views-multiple-inline-formsets/ to setup my site. I was wondering how to save the user field automatically to an inline formset in views (I used the blockquote for changes to the original). The RecipeForm in (see also below for context)
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
saves nicely automatically but not the
ingredient_form.owner= self.request.user
I know Django suggests using BaseInlineFormSet, but most people suggest saving user field in views.py and not forms or models for many different reasons. I would appreciate any suggestions or answers. Here's the full code:
models.py
from django.db import models
class Recipe(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length=255)
description = models.TextField()
class Ingredient(models.Model):
owner = models.ForeignKey(User)
recipe = models.ForeignKey(Recipe)
description = models.CharField(max_length=255)
class Instruction(models.Model):
recipe = models.ForeignKey(Recipe)
number = models.PositiveSmallIntegerField()
description = models.TextField()
forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
IngredientFormSet = inlineformset_factory(Recipe, Ingredient)
InstructionFormSet = inlineformset_factory(Recipe, Instruction)
views.py
from django.http import HttpResponseRedirect
from django.views.generic import CreateView
from .forms import IngredientFormSet, InstructionFormSet, RecipeForm
from .models import Recipe
class RecipeCreateView(CreateView):
template_name = 'recipe_add.html'
model = Recipe
form_class = RecipeForm
success_url = 'success/'
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet()
instruction_form = InstructionFormSet()
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST)
instruction_form = InstructionFormSet(self.request.POST)
if (form.is_valid() and ingredient_form.is_valid() and
instruction_form.is_valid()):
return self.form_valid(form, ingredient_form, instruction_form)
else:
return self.form_invalid(form, ingredient_form, instruction_form)
def form_valid(self, form, ingredient_form, instruction_form):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
ingredient_form.instance = self.object
ingredient_form.owner= self.request.user
ingredient_form.save()
instruction_form.instance = self.object
instruction_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form, instruction_form):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
I did some more research and the solution looks somewhat complex following this guide of how to add custom formset saving but modified for BaseInlineFormset as mentioned above. I realized it will be simpler just to make ModelForms for each Model and then linking them in a view, since I only need one child form at a time in the add a new recipe view and can reuse the ModelForm code.
here's the new code that works great! Feel free to contact if you need more info.
forms.py
from django.forms import ModelForm
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
exclude = ['owner',]
class IngredientForm(ModelForm):
class Meta:
model = Ingredient
exclude = ['owner','recipe',]
class InstructionForm(ModelForm):
class Meta:
model = Instruction
exclude = ['recipe',]
views.py
from .forms import IngredientForm, InstructionForm, RecipeForm
def add_new_value(request):
rform = RecipeForm(request.POST or None)
iform = IngredientForm(request.POST or None)
cform = InstructionForm(request.POST or None)
if rform.is_valid() and iform.is_valid() and cform.is_valid():
rinstance = rform.save(commit=False)
iinstance = iform.save(commit=False)
cinstance = cform.save(commit=False)
user = request.user
rinstance.owner = user
rinstance.save()
iinstance.owner = user
cinstance.owner = user
iinstance.recipe_id = rinstance.id
cinstance.recipe_id = rinstance.id
iinstance.save()
cinstance.save()
return HttpResponseRedirect('/admin/')
context = {
'rform' : rform,
'iform' : iform,
'cform' : cform,
}
return render(request, "add_new_recipe.html", context)
template: add_new_recipe.html
<!DOCTYPE html>
<html>
<head>
<title>Add Recipe</title>
</head>
<body>
<div>
<h1>Add Recipe</h1>
<form action="" method="post">
{% csrf_token %}
<div>
{{ rform.as_p }}
{{ iform.as_p }}
{{ cform.as_p }}
</div>
<input type="submit" value="Add recipe" class="submit" />
</form>
</div>
</body>
</html>

Categories