I am building a web-app where people can write projects. The projects are stored in a model and I want to use the user as the foreign key so I can show the user their projects on a webpage. Instances are entered through a form.
The code always assigns the default value (1) and and not the user. Can any of you see what's causing this bug?
Here is the code for the creation of the model in models.py:
class PersonalProject(models.Model):
user = models.ForeignKey(User, default=1, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField()
code = models.TextField()
def __str__(self):
return self.title
Heres the code for the form view to create the project in views.py:
def newproject(request):
if User is None:
messages.error(request, "Must be signed in")
return redirect('main:dashboard')
if request.method == "POST":
form = NewProjectForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('main:dashboard')
else:
messages.error(request, "Error")
else:
form = NewProjectForm()
return render(request,
"main/newproject.html",
{"form":form})
Heres the code for the homepage view in views.py:
def dashboard(request):
messages.info(request, request.user.username)
return render(request=request,
template_name="main/dashboard.html",
context={"structuredprojects": StructuredProject.objects.all(), "personalprojects": PersonalProject.objects.filter(user__username=request.user.username)})
I really hope you can help - I've been stuck on this for a while
You can set the user of the instance wrapped in the form to request.user:
from django.contrib.auth.decorators import login_required
#login_required
def newproject(request):
if request.method == "POST":
form = NewProjectForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('main:dashboard')
else:
messages.error(request, "Error")
else:
form = NewProjectForm()
return render(request,
"main/newproject.html",
{"form":form})
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Related
I already have seen this bug in other post, but still in trouble.
I'm trying to create a social network like instagram where users will be able to publish posts (photos).
I have User class which herit from AbstractUser, and got a OneToMany field of posts: each user can publish many posts.
After successfully pulling my photo from: PostForm(request.POST, request.FILES) and saving it correctly, I cannot add this photo to the current user's publications/posts and got error:
'NoneType' object has no attribute 'add'
def blog_and_photo_upload(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user = get_user(request) # user instance is correct with good pk
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.save()
user.posts.add(post) # row doesnt work
redirect('home')
return render(request, 'base/upload_post.html', {'form': form})
models.py
class Post(models.Model):
...
image = ResizedImageField(size=[300, 300], blank=True, upload_to='posts')
class User(AbstractUser):
...
posts = models.ForeignKey(Post, on_delete=models.Cascade, null=True)
You can simply update the form like this:
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.posts = post
user.save()
return redirect('home')
But, I think the design of the model is wrong, User to Post relation should be like this:
Class User(...):
posts = models.ManyToManyField(Post)
In that way, your original implementation should work. (Probably you don't need user.save() call in your view).
At first there should be return redirect(...) not only redirect() and secondly try to use the following view:
def blog_and_photo_upload(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user = get_user(request) # user instance is correct with good pk
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.posts.add(post) # add post to user's posts field
user.save()
return redirect('home')
return render(request, 'base/upload_post.html', {'form': form})
You need to bind first Post with User model like add a ForeignKey or a ManyToManyFields to relate them
posts = models.ForeignKey(User)
then you will be able to call it like you did
user.posts # this won't return None
Check this many to many field docs: https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/
I have create a model form using also OneToOneField field to link it to my user, my problem is that I have to go to the admin section to manually select the user to make it works. Is it possible to have the user linked to the form submitted automatically?
Also this may be stupid but when I use user.userinformation.gender to get the info about gender I get back "m", is there a way to access the label "Male" instead? TThank for yours help!
My code:
models.py
class UserInformation(models.Model):
gender_choice = [('m', 'Male'),('f', 'Female')]
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.CASCADE)
first_name = models.CharField(max_length=120)
last_name = models.CharField(max_length=120)
gender = models.CharField(max_length=120, choices=gender_choice)
phone = models.CharField(max_length=10)
email = models.EmailField()
forms.py
class UserInformationForm(forms.ModelForm):
class Meta:
model = UserInformation
fields = ('first_name', 'last_name', 'gender', 'phone', 'email',)
views.py
def add_information(request):
if request.method == "POST":
form = UserInformationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Form submitted')
else:
form = UserInformationForm()
return render(request, 'accounts/profile/add_information.html', {'form': form})
def show_profile(request):
profile_info = UserInformation.objects.all()
return render(request, 'accounts/profile/user_profile.html', {'profile_info': profile_info})
Yes, you can link the .user of the UserInformation to the logged in user with:
from django.shorcuts import redirect
from django.contrib.auth.decorators import login_required
#login_required
def add_information(request):
if request.method == 'POST':
form = UserInformationForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
messages.success(request, 'Form submitted')
return redirect('name-of-some-view')
else:
form = UserInformationForm()
return render(request, 'accounts/profile/add_information.html', {'form': form})
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
I think that what you are looking for is the initial attribute of UserInformationForm instance but you need to add user to the form fields.
form = UserInformationForm(initial={"user":request.user.id})
If you dont want your user to see/edit the information, consider using a HiddenInput widget in your ModelForm or set disabled attribute.
class UserInformationForm(forms.ModelForm):
class Meta:
model = UserInformation
fields = '__all__'
widgets = {'user': forms.HiddenInput()}
What I'm trying to do?
I want to display 2 registration forms separately of each other on the same page. The forms are: built-in User model and my self created UserProfile. To track, on what form user is now, I use sessions. It some sort of flags for me at the moment.
Why I don't want to use sessions?
I discovered a 'bug', at least for me, that I don't know how to fix. Bug appears if user passed first registration form, and close browser/tab. Next time user opens registration page, it will show second registration form, instead of first, as expected.
Where bug happens, but now with code.
When user opens register page first time, built-in UserCreationForm will be show, because there is no session called username yet.
def get(self, request):
if request.session.get("username", None):
self.context["form"] = RegisterProfile()
else:
self.context["form"] = UserCreationForm()
I'm using CBV, so it's OK that function called get and first argument is self. Also I created context dictionary as instance variable, so I can just add new field form to it.
Next, if user fill in given form (note, that first form is built-in User's form) built-in User instance will be created and it's username will be stored in username session.
If you confused at the moment don't worry much, I leave full view code at the bottom.
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
request.session["username"] = request.POST["username"]
return redirect("register")
Now, when session username exsists, and user redirected to same view, my self-created form will be shown. As in first code example.
Now, bug happens. User can freely leave page, and when he come back, second registration form will be shown again. That's not what I want.
Full view code:
class Register(View):
context = {"title": "Register new account"}
def get(self, request):
if request.session.get("username", None):
self.context["form"] = RegisterProfile()
else:
self.context["form"] = UserCreationForm()
return render(request, "users/register.html", context=self.context)
def post(self, request):
if request.session.get("username", None):
form = RegisterProfile(request.POST, request.FILES)
if form.is_valid():
username = request.session.get("username", None)
if not username:
messages.error(request, "We are sorry, but error happend. Try again!")
return redirect("index")
user = User.objects.filter(username=username).first()
profile = UserProfile(
user=user,
nickname=request.POST["nickname"],
sex=request.POST["sex"],
age=request.POST["age"],
profile_picture=form.files["profile_picture"],
)
profile.save()
del request.session["username"]
messages.success(request, "Profile created successfully!")
return redirect("index")
else:
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
request.session["username"] = request.POST["username"]
return redirect("register")
self.context["form"] = form
return render(request, "users/register.html", context=self.context)
UPD 1:
I changed register logic a little, now full code looks like this:
class Register(View):
context = {"title": "Register user page"}
def get(self, request):
if request.session.get("user_data", None):
form = ProfileRegisterForm()
else:
form = UserCreationForm()
self.context["form"] = form
return render(request, "users/register.html", context=self.context)
def post(self, request):
if request.session.get("user_data", None):
form = ProfileRegisterForm(request.POST, request.FILES)
if form.is_valid():
user = User.objects.create_user(*request.session["user_data"])
user.save()
UserProfile.objects.create(
user=user,
nickname=request.POST["nickname"],
sex=request.POST["sex"],
age=request.POST["age"],
profile_picture=form.files["profile_picture"],
)
del request.session["user_data"]
messages.success(request, "Profile created successfully!")
return redirect("index")
else:
form = UserCreationForm(request.POST)
if form.is_valid():
request.session["user_data"] = [
request.POST["username"],
request.POST["password1"],
request.POST["password2"]
]
return redirect("register")
self.context["form"] = form
return redirect("register")
But anyway, I need place to store temporary data like username, password1 and password2. If someone knows, where I can store data like in sessions, please, answer bellow.
I'm working my way through Django and I'm creating an app that will allow users to use an ID number to sign-in to a system. So I have two views, one for users to log-in, and the other to sign-up. The former view works just fine, I can get it to display the information the user has submitted. However I can't get the second view to display the POST data to the user:
from .forms import NameForm, IdForm
from django.shortcuts import render
from django.http import HttpResponse
def sign_in(request):
if request.method == "POST":
#here will construct the form with the POST data
form = NameForm(request.POST)
#the next part is to check that the information submitted is valid
if form.is_valid():
post = form.save()
post.save()
return HttpResponse(post)
else:
return HttpResponse("Form is invalid")
else:
form = NameForm()
return render(request, 'checkin/base.html', {'form': form})
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
return HttpResponse(post)
else:
return HttpResponse('Form is invalid')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
Basically I want to make the response to be "thank you, your ID number is: post".
Here is the class for my model:
from __future__ import unicode_literals
from django.db import models
from django import forms
from django.forms import ModelForm
# Create your models here.
class Question(models.Model):
question_text = models.CharField("What is your ID?", max_length=200)
id_text = models.CharField("Enter a new identification
number",max_length=200, null=True)
def __str__(self):
return self.question_text
And here are the form classes for both views:
from django.forms import ModelForm
from .models import Question
#put the form here
class NameForm(ModelForm):
class Meta:
model = Question
fields = ['question_text']
class IdForm(ModelForm):
class Meta:
model = Question
fields = ['id_text']
It's not generally acceptable to display the POST data as the respnose to the user. That's not HTML, merely a dictionary which the average user will not understand. The standard way of using forms in Django (and indeed almost any web framework) is to display the form validation errors to the user so that he may rectify it.
The right way
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
return HttpResponseRedirect('/succes_url')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
The problem is in line return HttpResponse(post),You are passing a whole form into HttpResponse,but as you mentioned,you just need id_text field of the IdForm.
So the updated code should be :
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
id = post.id_text
return HttpResponse('thank you, your ID number is: '+id)
else:
return HttpResponse('Form is invalid')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
I'm using django built in user creation form which I extended like this
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.TextInput(attrs={'placeholder': 'E-mail address'}))
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'username', 'password1', 'password2')
def clean_email(self):
email = self.cleaned_data["email"]
try:
User._default_manager.get(email=email)
except User.DoesNotExist:
return email
#raise forms.ValidationError(self.error_messages['duplicate_email'])
raise forms.ValidationError('duplicate_email')
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
As you see I've added email validation to check if there is already user with that email (what I don't accept).
The problem is that neither that of those errors are displaying in my template and I don't know why. I've tried with {{form.field.errors}} and even with {{form.errors.values.[i]}} but nothing helps
I looked in auth.forms to check class UserCreationForm and I see that it pass errors to error_messages but I couldn't find the way to display them.
Although all validations (username check, password check, email check) work fine I'd still like to display error above field with error so that user understands why he couldn't create his account.
UPDATE
views.py
def register_user(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
args = {}
args.update(csrf(request))
args['form'] = RegistrationForm()
return render_to_response('user_profile/register.html', args, context_instance=RequestContext(request))
I hope somebody can help me with that.
Thanks
This will work:
def register_user(request):
args = {}
args.update(csrf(request))
if request.method == 'POST':
form = RegistrationForm(request.POST)
args['form'] = form
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
else:
args['form'] = RegistrationForm()
return render_to_response('user_profile/register.html', args, context_instance=RequestContext(request))
The problem with your code was that you was passing an unbound form instance to your template all the time, so you was just overwriting any occured errors.
Look here for a reference: docs
If you are looking at more efficient solution then you should try my below code in views.py file.
Your forms.py code will be same. But it is leakage of validation of password.
# to register auth user
class register_user(View):
template1 = "app_name/register.html" # define templates
template2 = "app_name/login.html"
def get(self, request): # get method
form = RegistrationForm()
return render(request, self.template1, locals())
def post(self, request): # post method
form = RegistrationForm(request.POST) # create form object
if form.is_valid():
print "Form is validated"
user_data = form.save(commit=False)
user_data.save()
return render(request, self.template2, locals())
else:
print "with error"
return render(request, self.template1, locals())
I hope this will help. I'm still working on good practice of Django.