I'm relatively new to Django and am stuck on this. I've been trying to create a website where users can create their accounts, view their and other member's profile pages.
models.py
class UserProfileInfo(models.Model):
"""docstring for ClassName"""
user = models.OneToOneField(User)
status = models.TextField(blank = True)
desc = models.TextField(blank = True)
portfolio_site = models.URLField(blank = True)
profilepic = models.ImageField(blank = True, null = True, upload_to = 'profile_pics')
def __str__(self):
return self.user.username
forms.py
class UserForm(forms.ModelForm):
password = forms.CharField(widget = forms.PasswordInput())
class Meta():
model = User
fields = ('username', 'email', 'password')
class UserProfileInfoForm(forms.ModelForm):
class Meta():
model = UserProfileInfo
fields = ('desc', 'portfolio_site', 'status', 'profilepic')
So, I want the user to be able to update their status from their homepage by typing into a text box.
index.html (the homepage from where the user can update their status)
<form method="POST" action="{% url 'app5:user_status' user.username %}">
{% csrf_token %}
<div class="form-group">
<label for="inputlg"></label>
<input class="form-control input-lg" id="inputlg" type="text" name='status' placeholder="How are you feeling?">
</div><center>
<input type="submit" name='submitbtn' value='Add Status'>
</center>
</form>
Here's what my function to update the status in views.py looks like:
def user_status(request, username):
if request.method == 'POST':
mystatus = request.POST.get('status')
user = User.objects.get(username=username)
user.userprofileinfo.status = mystatus
user.save()
return redirect('app5:profilepage', username=username)
However, it does not work. If I try to update some other field like the username or email which is in the User Model instead of the UserProfileInfo extended model, user.email = mystatus works in that case. But the update doesn't take place when I try to update the status field of UserProfileInfo model. I'm pretty sure the error exists in the line: user.userprofileinfo.status= mystatus. I've searched in the documentation and many other sources but have been unable to find an answer.. How do I go about this?
Any help would be greatly appreciated. Thank you!
Edit: I do not get any error when I click on Submit. It's just that the updation doesn't take place in the model and the status field stays the same as before.
You saved the user, not the userprofileinfo object. It's a separate model, so needs to be saved separately.
profile = user.userprofileinfo
profile.status = mystatus
profile.save()
if you have update field in UserProfileInfo then get the object for UserProfileInfo and update the field as below
def user_status(request,username):
if request.method == 'POST':
mystatus = request.POST.get('status')
user = User.objects.get(username=username)
userprofileinfo = user.userprofileinfo
userprofileinfo.status = mystatus
userprofileinfo.save()
return redirect('app5:profilepage',username=username)
Related
I am trying to create an e-commerce website, in which I have used the functionality of the Django authentication to allow users to register and login. However, in the registration form, I take the information from 2 tables at once, like this...
In my models.py...
class Profile (models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
mobileNumber = models.CharField(max_length=13)
address = models.ForeignKey(Shipping, on_delete=models.DO_NOTHING, default=False)
guest = models.BooleanField(default=False)
class Shipping(models.Model):
addressLine1 = models.CharField(max_length=100)
addressLine2 = models.CharField(max_length=100)
city = models.CharField(max_length=40)
postalCode = models.CharField(max_length=5)
landmark = models.CharField(blank=True, max_length=80)
And my forms.py...
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
from store.models import Shipping
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
first_name = forms.CharField()
last_name = forms.CharField()
mobile_number = forms.CharField()
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'mobile_number', 'email', 'password1', 'password2']
class AddressRegisterForm(forms.ModelForm):
class Meta:
model = Shipping
fields = ['addressLine1', 'addressLine2', 'city', 'postalCode', 'landmark']
My signals.py...
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
My views.py...
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm
from .forms import AddressRegisterForm
from .models import Profile
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from . import signals
import django.dispatch
# Create your views here.
def register(request):
if not request.user.username :
if request.method == 'POST':
form = UserRegisterForm(request.POST)
form1 = AddressRegisterForm(request.POST)
if form.is_valid() and form1.is_valid():
email = form.cleaned_data.get('email')
if not User.objects.filter(email=email).exists():
username = form.cleaned_data.get('username')
messages.success(request, f"The Account created for {username}! You are now able to login.")
form1.save()
form.save()
return redirect('login')
else:
messages.warning(request, f"The email is either invalid or already used to create an account")
else:
form1 = AddressRegisterForm()
form = UserRegisterForm()
return render (request, 'register.html', {"form":form, "form1":form1})
else:
username = request.user.username
messages.warning(request, f"You are already logged in as {username}!")
return redirect ('home')
And finally my template...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load static %}
{% block title %}Register{% endblock %}
{% block content %}
<div class="content-section mb-5">
<form method="POST">{% csrf_token %}
<div style="display: flex; width: 100%; justify-content: space-around;">
<fieldset class="form-group mt-4" style="width: 48%;">
<legend class="border-bottom mb-4">Register</legend>
{{ form | crispy }}
</fieldset>
<fieldset class="form-group mt-3" style="width: 48%;">
<legend class="border-bottom mb-4">Shipping info</legend>
{{ form1 | crispy }}
</fieldset>
</div>
<div class="form-group mt-3">
<button class="btn btn-outline-info" type="submit">Sign up</button>
<small class="text-muted ml-3">Forgot Password?</small>
</div>
</form>
<div class="border-top pt-3">
<small class="text-muted">Already have an account? Sign in</small>
</div>
</div>
{% endblock %}
My information gets stored in the relevant tables, however, there seems to be no effect on the Profile table and remains to be empty...
Is there any way I can fire off django to link the user and the address in the Profile table once the user is registered.
Any help would be appreciated,
Thanks a lot!!
You need to load the signals in the ready() function of your app configuration:
Where should this code live?
Strictly speaking, signal handling and registration code can live anywhere you like, although it’s recommended to avoid the application’s root module and its models module to minimize side-effects of importing code.
In practice, signal handlers are usually defined in a signals submodule of the application they relate to. Signal receivers are connected in the ready() method of your application configuration class. If you’re using the receiver() decorator, import the signals submodule inside ready().
yourapp/apps.py:
class YourAppConfig(AppConfig):
name = "yourapp"
def ready(self):
from . import signals
That should be enough to get the signals registered at the correct time.
Regarding two issues:
Existing user models in your database won't get updated, only new ones. Either drop your db and create new users or add a data migration if your database is live or you want to keep the current.
Models connected by signals must be created without having any data for them. So you either must have nullable fields or fields with defaults.
Two forms
So you've posted 2 forms as one form in the HTML, this works because your forms don't share field names (except primary key, which isn't posted). For future reference, you can use a form prefix to deal with duplicate field names and even use the same form for different purposes (shipping and billing address for example).
On signals
Signals are used when an event happens (user is saved, user logs in), that is part of 3rd party code (in this case Django). But when you're the one creating the event, they are not needed as you can just do whatever you want right at where you create the event.
Logged in or not
The correct test is:
if not request.user.is_authenticated:
On to the solution
When saving related models with one or more forms, you should first create the models that can be saved without the other model of the relationship existing. These are:
The model a foreign key points to (Shipping).
The model of a OneToOneField, where the OneToOneField is not defined (User)
Due to the signal, our profile model will be created with empty values, but the form does not know that the mobile_number belongs on the Profile model, because it is a ModelForm bound to the User Model. It treats mobile_number as a field that you will handle yourself. So let's do that:
user = form.save() # save returns the model instance created
profile = user.profile
profile.mobile_number = form.cleaned_data['mobile_number']
As said, we can create the address without problems and then we can link it to the profile:
address = form1.save() # save returns the model instance created
profile.address = address
And now we can save the profile:
profile.save()
So putting it all together:
def register(request):
if not request.user.is_authenticated: # The correct way to test for logged in user
if request.method == "POST":
form = UserRegisterForm(request.POST)
form1 = AddressRegisterForm(request.POST)
if form.is_valid() and form1.is_valid():
email = form.cleaned_data.get("email")
if not User.objects.filter(email=email).exists():
username = form.cleaned_data.get("username")
messages.success(
request,
f"The Account created for {username}! You are now able to login.",
)
user = form.save()
profile = user.profile
profile.mobile_number = form.cleaned_data['mobile_number']
address = form1.save()
profile.address = address
profile.save()
return redirect("login")
else:
messages.warning(
request,
f"The email is either invalid or already used to create an account",
)
else:
form1 = AddressRegisterForm()
form = UserRegisterForm()
return render(request, "register.html", {"form": form, "form1": form1})
else:
username = request.user.username
messages.warning(request, f"You are already logged in as {username}!")
return redirect("home")
You can add all the fields related to the shipping address in the "profile" model.
When you want to access that data, you can simply use querysets.
In the models
class Profile (models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
mobileNumber = models.CharField(max_length=13)
address = models.ForeignKey(Shipping, on_delete=models.DO_NOTHING, default=False)
guest = models.BooleanField(default=False)
addressLine1 = models.CharField(max_length=100)
addressLine2 = models.CharField(max_length=100)
city = models.CharField(max_length=40)
postalCode = models.CharField(max_length=5)
landmark = models.CharField(blank=True, max_length=80)
and when you want to access the data, you can simply use querysets
I am new to Django and i am working on a website where user can submit a post. Django Form is not saving in database when i have manytomany field in model. I do not know if i can achieve this in Django, I want to attached other user names to the post so that when i submit the form the user name is selected automatically when i check on the post in admin. I will attach a screenshots for clarity.
This image below is my form, as you can see 'My post' is the image_caption while 'andy' is another user name, i want 'andy' selected automatically in manytomany field when form is submitted.
This is what i want when the form is submitted then i check in admin. The other user name (andy) is selected in manytomany field when the form is submitted. I did this manually
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/user-img.png", blank=True)
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
image_caption = models.TextField(blank=True, null=True)
tag_someone = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='tagged_users', blank=True)
Forms:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = (
'image_caption',
'tag_someone',
)
Views:
def upload_view(request):
ImageFormset = modelformset_factory(File, fields=('files',), extra=20)
if request.method == "POST":
form = PostForm(request.POST)
formset = ImageFormset(request.POST, request.FILES)
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.poster_profile = request.user
post.save()
form.save_m2m()
for f in formset:
try:
photo = File(post=post, files=f.cleaned_data['files'])
photo.save()
except Exception as e:
break
return redirect('/')
else:
form = PostForm()
formset = ImageFormset(queryset=File.objects.none())
#User Name Auto-complete In Tag Form
all_users = User.objects.values_list('username', flat=True)
context = {
'form': form,
'formset': formset,
'all_users': all_users,
}
return render(request, 'upload.html', context)
Upload.html:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
{{ formset }}
<button type="submit" class="btn btn-primary btn-sm btn-block w-25">Post</button>
</form>
I was able to get this working by changing widget to:
widget = {
forms.Select(),
}
You can change the widget here. Widgets are the thing that is responsible for outputting the inputs to HTML. So you just need:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = (
'image_caption',
'tag_someone',
)
widgets = {'tag_someone': forms.TextInput}
I am trying to get Seller information, username, password, name, mobile number and address. I have used User to get the username and password, connected the username to my model through OneToOneField relationship and later save the information.
I have made a model named SellerDetails which gets the user field from OneToOneField with User and rest all details are provided as follows:
#models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class SellerDetails(models.Model):
"""docstring for SellerDetails."""
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=128, null = True)
address = models.CharField(max_length=256)
mobile_number = models.IntegerField(primary_key=True)
def __str__(self):
return self.user.username
then I have made two forms in my forms.py
#forms.py
from django import forms
from django.contrib.auth.models import User
from avkara.models import SellerDetails, VendorDetails
class UserForm(forms.ModelForm):
password = forms.CharField(widget = forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'password')
class SellerDetailsForm(forms.ModelForm):
"""docstring for SellerDetailsForm."""
class Meta:
model = SellerDetails
fields = ('name','address','mobile_number')
#Then I tried established a relation while saving the form
#views.py
def Signup_seller(request):
seller_registered = False
if request.method == "POST":
user_form = UserForm(data = request.POST)
user_details = SellerDetailsForm(data = request.POST)
if user_form.is_valid() and user_details.is_valid():
seller = user_form.save()
seller.set_password(seller.password)
seller.save()
#saving user_details now
other_details = user_details.save(commit= False)
other_details.user = seller
other_details.save()
seller_registered = True
else:
user_form = UserForm()
user_details = SellerDetailsForm()
return render(request, 'avkara/signup_seller.html',{'seller_registered': seller_registered , 'user_form' : user_form, 'user_details' : user_details})
#Then I tried serving both forms through Html form. Here is my html
#signup_seller.html
<div class="container jumbotron">
<h2>Sign up</h2><br><br><br>
<form class="form-horizontal" method="POST">
{% csrf_token %}
{{ user_form.as_p }}
{{ user_details.as_p }}
<input type="submit" value="Submit">
</form>
</div>
#The desired out should be Thank you for registering but I get following error
IntegrityError at /avkara/signup_seller
NOT NULL constraint failed: avkara_sellerdetails.password
#This is error it is showing
F:\Webpage\python\techforstock\avkara\views.py in Signup_seller
other_details.save() <!--error part --!>
I have worked one it for hours, but no luck, Can Anyone help me out with this.
I want the username and password to be registered in users section and other information saved in Sellerdetails when I open Admin interface, but it should be with respect to the specific username.
Django user profile model form data is not getting displayed on the template, not even giving me an error!
I am learning to create Django registration form with user profile models I have created the registration form and profile form successfully but I am not getting any values from models.py file.
Models.py
class ExtenduserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birth_date = models.DateField(null=True, blank=True)
age = models.IntegerField()
def __str__(self):
return self.user.username
#receiver(post_save,sender=User)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile =
ExtenduserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
forms.py
class userprofile(forms.ModelForm):
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
class Meta:
model = ExtenduserProfile
fields = ('age','birth_date')
views.py
#login_required()
def UserProfile(request,pk=None):
profile = ExtenduserProfile.objects.all()
return render(request,'dashboard/profile.html',{'profile':profile})
#login_required()
def HomeScreen(request):
if request.user.is_authenticated:
username = request.user.username
else :
username = 'not logged in'
context = {'username':username,'user':user}
return render(request,'dashboard/Home.html',context)
def singup(request):
if request.method == 'POST':
form = SignupForm(request.POST)
user_profile = userprofile(request.POST)
if form.is_valid() and user_profile.is_valid():
user = form.save()
profile = user_profile.save(commit=False)
profile.user = user
profile.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username = username, password = password)
login(request,user)
return redirect(login_view)
else:
form = SignupForm()
user_profile = userprofile()
context = {'form':form, 'user_profile':user_profile }
return render(request,'account/signup.html',context)
HTML file
{% extends 'dashboard/base.html' %}
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Profile</title>
</head>
{% block content%}
<body>
<h3>Welcome to profile page</h3><br>
{% if user.is_authenticated %}
<h4>Name = {{user.first_name}} {{user.last_name}}</h4>
<h4>Email = {{user.email}}</h4>
<h4>Age = {{user.ExtenduserProfile.age}} </h4>
<h4>DOB = {{user.ExtenduserProfile.birth_date}} </h4>
{% endif %}
</body>
{% endblock%}
</html>
my expected output should be
name: xyz
email: abc#abc.com
age: 24
DOB: 1994-04-21
What is a Custom User Model Extending AbstractUser?
It is a new User model that inherit from AbstractUser. It requires a special care and to update some references through the settings.py. Ideally it should be done in the begining of the project, since it will dramatically impact the database schema. Extra care while implementing it.
When should I use a Custom User Model Extending AbstractUser?
You should use it when you are perfectly happy with how Django handles the authentication process and you wouldn’t change anything on it. Yet, you want to add some extra information directly in the User model, without having to create an extra class.
I suggest to you to use Abstract User
Which is easier and best practice for your case
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
Also add this to your setting :
AUTH_USER_MODEL = 'appname.User'
Check this tutorial for more information
Well, when you are using reverse relation in OneToOne, the class name should be in all lowercase(documentation). For example:
<h4>Age = {{user.extenduserprofile.age}} </h4>
<h4>DOB = {{user.extenduserprofile.birth_date}} </h4>
Also, why are you sending profile = ExtenduserProfile.objects.all() to template via context from your profile view? Because as I can see, you are not using it. So I think you can remove that part of code as well.
Also, you can remove the {% if user.is_authenticated %} and {%endif%} from template. As, this template can't be accessible unless you are logged in. You have restricted that via login_required decorator.
I have a view which renders 2 forms to a template, but only one renders, the other doesnt display and it doesnt give me any error, but I can see that the form display when I print it in my console.
This is my model for the form not showing
class Organization(models.Model):
name = models.CharField(max_length=255, null=True)
This is the model for the admin, Im inheriting from AbstractUSer
class User(AbstractUser):
is_user = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
This is the form for the model
class OrganizationForm(forms.ModelForm):
name = forms.CharField(max_length=255)
class Meta:
model = Organization
fields = ['name']
This is the form for the Admin
class AdminSignUpForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = ['username','email']
def save(self, commit=True):
user = super().save(commit=False)
user.is_admin = True
if commit:
user.save()
return user
This is the view which I am calling the multiple forms
def signup(request):
if request.method == 'POST':
adminForm = AdminSignUpForm(request.POST)
orgForm = OrganizationForm(request.POST)
if adminForm.is_valid() and orgForm.is_valid():
adminForm.save()
orgForm.save(commit=False)
username = adminForm.cleaned_data.get('username')
raw_password = adminForm.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
login(request, user)
return redirect('myapp:home')
else:
adminForm = AdminSignUpForm()
orgForm = OrganizationForm()
print(orgForm)
return render(request, 'registration/signup_form.html', {'OrgFrom': orgForm,'Adminform': adminForm})
And this is the template I am rendering the multiple forms
<form enctype="multipart/form-data" method="post" >
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{{Adminform.as_p }}
{{ OrgForm.as_p }}
<button type="submit" class="btn btn-success">Sign up</button>
</form>
I expect both forms to be displayed but only the Adminform displays and it gives me no error to work with
There is one typo. You need to use {{ OrgFrom.as_p }} instead of {{ Orgform.as_p }}.