What a want to do: When a user is logged in, and he or she makes a post, the name of that user should automatically be assigned in my database posts.
What it's doing: It's not adding a user automatically, but i am able to assign a user manually, so I'm accessing the user database, and seeing whom i can attach to a newly made post.
My question is then, how can i get this process done automatically?
Here is my code from the model.py in the posts app:
from __future__ import unicode_literals
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.contrib.auth.models import User
User = settings.AUTH_USER_MODEL
class Post(models.Model):
title = models.CharField(max_length=120)
content = models.TextField()
#email = models.EmailField(null=True, blank=True, default=None)
user = models.ForeignKey(User, null=True,)
#upload = models.FileField(null=True, blank=True, default=None)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=True, auto_now_add=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:detail", kwargs={"id":self.id})
class Meta:
ordering = ["-timestamp", "-updated"]
I am getting the user class via User = settings.AUTH_USER_MODEL and the AUTH_USER_MODEL is referring in settings.py to a class called MyUser in another models.py who originates from an app called accounts.
here is the code from that class:
class MyUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
is_admin = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin site.'))
is_active = models.BooleanField(_('active'), default=True,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
USERNAME_FIELD = 'email'
Here is the code from views.py in the posts app:
def post_create(request):
form = PostForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
# Message succes
messages.success(request, "Succesfully Created ")
return HttpResponseRedirect(instance.get_absolute_url())
else:
messages.error(request, "Not Succesfully created")
context = {
'form': form,
}
return render(request, app_name+"/post_form.html", context)
Here is the forms.py in the posts app:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = {
"title",
"content",
"user",
#"email",
#"upload",
}
Here are two pictures to illustrate my problem:
The post create site
The django administration
Let me now if any more code is needed, appreciate any feedback as well.
I don't have a lot of rep on stack overflow so please let me know if this is poorly explained, and i shall re right it.
Simply change:
instance = form.save(commit=False)
instance.save()
to
instance = form.save(commit=False)
if request.user.is_authenticated():
instance.user = request.user
instance.save()
If user is logged in, i think the Combobox should not
appear, so you can do that on forms.py
forms.py
class PostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(PostForm, self).__init__(*args, **kwargs)
if not self.user.is_authenticated():
self.fields['user'] = forms.ModelChoiceField(
required=True,
queryset=User.objects.all())
class Meta:
model = Post
fields = {
"title",
"content",
# "user",
#"email",
#"upload",
}
on views.py
def post_create(request):
form = PostForm(request.POST or None, user = request.user)
if form.is_valid():
if request.user.is_authenticated():
form.instance.user = request.user
form.save()
...
return render(request, app_name+"/post_form.html", context)
If you want the Combobox has selected with the user logged in, you can pass initial data on views.py, like this:
def post_create(request):
if request.method == 'GET':
form = PostForm(initial = {'user' : request.user})
Related
I'm making a site in Django using django-allauth for authentication.
I've created a custom user class in accounts.models to add a custom field, FavouriteTeam.
The issue I have is that the form renders fine and submits formdata for fav_team fine (as inspected in Chrome dev tools) but the fav_team entry doesn't get stored to user.FavouriteTeam and I can't figure out why.
I can go into the Django shell, import my User class from accounts.models, query for a user, add a .FavouriteTeam, and save just fine. It's just that the form doesn't seem to save the data into the new User instance for some reason.
I'm guessing it's due to the way django-allauth interacts with custom user models but I can't figure it out for the life of me. I've seen some similar posts but none have a situation like this or have a solution that seems to work for me.
Any ideas?
accounts.models: -
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
FavouriteTeam = models.ForeignKey('predictor.Team', on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.email
accounts.forms: -
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
from allauth.account.forms import SignupForm
from predictor.models import Team
from django.contrib.auth import get_user_model
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='First Name')
last_name = forms.CharField(max_length=30, label='Last Name')
fav_team = forms.ModelChoiceField(queryset=Team.objects.all(), empty_label=None, label='Favourite Team')
class Meta:
model = get_user_model()
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.FavouriteTeam = self.cleaned_data['fav_team']
user.save()
return user
predictor.models: -
class Team(models.Model):
ShortName = models.CharField(max_length=4, primary_key=True)
Town = models.CharField(max_length=20)
Nickname = models.CharField(max_length=20)
Conference = models.CharField(max_length=3, null=True, blank=True)
Division = models.CharField(max_length=5, null=True, blank=True)
ConfDiv = models.CharField(max_length=9, null=True, blank=True)
Logo = models.ImageField(default='football.png', upload_to='logos')
def __str__(self):
return('{} {}'.format(self.Town, self.Nickname))
def save(self, *args, **kwargs):
self.ConfDiv = str(self.Conference)+" "+str(self.Division)
super(Team, self).save(*args, **kwargs)
For anyone looking at this and suffering the same issue, I eventually found the solution to be as below.
Firstly, I had to create adpaters.py within my accounts app and fill in the below: -
from allauth.account.adapter import DefaultAccountAdapter
class AccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=False):
data = form.cleaned_data
user.email = data['email']
user.first_name = data['first_name']
user.last_name = data['last_name']
user.FavouriteTeam = data['fav_team']
if 'password1' in data:
user.set_password(data['password1'])
else:
user.set_unusable_password()
self.populate_username(request, user)
user.save()
return user
Then I had to referenced the new account adapter in my project's settings.py file as below: -
ACCOUNT_ADAPTER = "accounts.adapters.AccountAdapter"
Hope that helps someone in the future.
I am creating a user using OneToOne relationship , when I enter the data in the form and submit it, I'm getting no null constraint error
view.py
def registerUser(request):
if request.method=='POST':
form=UserCreationForm(request.POST)
form_class=ProfileForm(request.POST)
if form.is_valid() and form_class.is_valid():
form.save()
form_class.save()
return redirect('/home/')
else:
form=UserCreationForm()
form_class = ProfileForm()
return render(request,'register.html',{'form':form,'form_class':form_class})
form.py
class Registerform(UserCreationForm):
class Meta:
model=User
fields= ['username','first_name','last_name','password1','password2']
def save(self, commit=True):
user=super(Registerform, self).save(commit=False)
user.first_name=self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
if commit:
user.save()
return user
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = [ 'location']
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
salary=models.CharField(max_length=120)
def __str__(self):
return self.location
Your UserProfile model requires a salary but your form only has a location field. So when your form is submitted with a location, it's valid, but the underlying model cannot be saved because salary will be None.
Add the salary field to your form, or make salary a nullable field.
Also, you need to assign the user field before saving the profile, since that also is not nullable. One way to do that is:
user = form.save() # this is the UserForm so when saving it returns the user
profile = form_class.save(commit=False)
profile.user = user
profile.save()
I would recommend you carefully read the Django documentation on model forms. The section on the save method in particular would explain you how to properly handle these cases.
I am trying to build an application that allows users to register and meet other users with similar interests. I am extending the user model using a OneToOne field but I run into a problem when I try to register some users: The profile does not save. The user data saves, but the profile data does not.
I do not understand what I am doing wrong, as I followed a tutorial to write the program.
This is my Models.py file:
class Profile(models.Model):
GENDERS = (
('M', 'Male'),
('F', 'Female'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
email = models.EmailField(max_length=254, blank=True)
gender = models.CharField(choices=GENDERS, max_length=1, null=True, default='')
dob = models.DateField(auto_now=False, auto_now_add=False, blank=True, null=True)
hobby = models.ManyToManyField(Hobby)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
post_save.connect(create_user_profile, sender=User)
This is my forms.py file:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'password', 'first_name', 'last_name')
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('email', 'gender', 'dob', 'hobby')
This is my view function:
def register(request):
if request.method =="POST":
userForm = UserForm(request.POST)
profileForm = ProfileForm(request.POST)
if userForm.is_valid() and profileForm.is_valid():
userForm.save()
profileForm.save()
return redirect('/')
else:
return render(request, 'QMLove/register.html', {'userForm': userForm, 'profileForm': profileForm})
else:
userForm = UserForm()
profileForm = ProfileForm()
return render(request, 'QMLove/register.html',{'userForm': userForm, 'profileForm': profileForm})
Thank you in advance!
You haven't done anything to associate the Profile you're creating with the User you've created. I would expect either two profiles two be created - one empty and one with data but not associated with the user - or for the profile form save to fail with an integrityerror because you didn't supply the user.
You should remove those signal receivers because they won't help with what you want to do and will probably create conflicts. Instead, pass the created user when you save the profile:
user = userForm.save()
profile = profileForm.save(commit=False)
profile.user = user
profile.save()
I have a website that has a registration system and a blog with some registered users. Yesterday, I added a new app that creates dedicated profile pages for each one of those users.
The issue being, the profile pages aren't getting created for the users that have already registered. This is, I guess because the user profile creation logic allows for profile creations only after the user has registered.
Below is the code in my models.py
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=User)
def create_account(sender, instance, created, *args, **kwargs):
if created:
profile, new = UserAccount.objects.get_or_create(user=instance)
post_save.connect(create_account, sender=settings.AUTH_USER_MODEL)
So, what can I do to create those profile pages?
I tried the following:
1. Manually creating a profile page against each registered users' username. (But this isn't the way I want to lean on. This is just a temporary arrangement)
Issue with this, when the superuser who creates these profile pages calls for his private profile page [at /u/], the code looks towards all the users created by the superuser instead of his own.
This is the error that's shown:
MultipleObjectsReturned at /u/
get() returned more than one UserAccount -- it returned 2!
.
.
.
instance = get_object_or_404(UserAccount)
So, what's the issue in code here? [my views.py]
# public user profile
def user_account(request, username):
instance = get_object_or_404(UserAccount, user__username=username)
context = {
'instance' : instance,
'title' : "User Account",
}
template = "user_accounts/public_account.html"
return render(request, template, context)
# private user profile.
#login_required
def self_user_account(request):
if not request.user.is_authenticated:
raise Http404
instance = get_object_or_404(UserAccount)
if not request.user == instance.user:
raise Http404
context = {
'instance' : instance,
'title' : 'Your Account',
}
template = "user_accounts/self_account.html"
return render(request, template, context)
# ability to update the user profile
#login_required
def update_user_account(request):
if not request.user.is_authenticated:
raise Http404
instance = get_object_or_404(UserAccount)
if not request.user == instance.user:
raise Http404
if request.method == 'POST':
form = UserAccountForm(request.POST or None, request.FILES or None, instance=instance)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
messages.success(request, "Account Updated.")
return HttpResponseRedirect("/u/")
else:
messages.error(request, "Something went wrong. Profile not created.")
else:
form = UserAccountForm(instance=instance)
context = {
'title': 'Update Your Account',
'form' : form,
}
template = "user_accounts/update.html"
return render(request, template, context)
Below is my urls.py These are populated after /u/ from the main urls.py
urlpatterns = [
# for updating
url(r'^update/$', views.update_user_account, name="update_user_account"),
# for outside world
url(r'^(?P<username>[\w.#+-]+)/$', views.user_account, name="public_user_account"),
# for the user himself
url(r'^$', views.self_user_account, name="self_user_account"),
]
Below is my models.py script
# uploading profile photos
def upload_location(instance, filename):
return "account_photos/%s/%s" %(instance.user, filename)
# Create your models here.
class UserAccount(models.Model):
user = models.OneToOneField(User)
photo = models.ImageField(
upload_to=upload_location, # there needs to be a upload location tho
# most probably a cdn server
blank=True,
null=True,
width_field="width_field",
height_field="height_field")
width_field = models.IntegerField(default=0, null=True)
height_field = models.IntegerField(default=0, null=True)
bio = models.TextField(max_length=60, null=True, blank=True, verbose_name="You in 60 words.")
phone = PhoneNumberField(blank=True, verbose_name="Contact Number")
status = models.CharField(max_length=128, default="Student")
totos = models.IntegerField(default=0, verbose_name="Contribution")
user_since = models.DateTimeField(auto_now=True)
# social links
email = models.EmailField(verbose_name="email address",
max_length=255,
unique=True,
null=True, blank=True)
custom_link = models.URLField(null=True, blank=True)
facebook_link = models.URLField(null=True, blank=True)
twitter_link = models.URLField(null=True, blank=True)
linkedin_link = models.URLField(null=True, blank=True)
github_link = models.URLField(null=True, blank=True)
reddit_link = models.URLField(null=True, blank=True)
def __str__(self):
return self.user.username
def get_absolute_url(self):
return self.reverse("accounts:public_user_account", kwargs={"username":self.user__username})
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=User) # User is coming from the user=models.OneToOneField(<User>)
def create_account(sender, instance, created, *args, **kwargs):
if created:
profile, new = UserAccount.objects.get_or_create(user=instance)
post_save.connect(create_account, sender=settings.AUTH_USER_MODEL)
Using get_object_or_404(UserAccount) uses all user accounts, so it will give a MultipleObjectsReturned error if more than one user account exists.
You need to filter the queryset so that it only returns one object. You are already using the username to do this. This should work as long as username is unique and there is only one user account per user.
get_object_or_404(UserAccount, user__username=username)
If you want to filter on the logged in user, you can do:
get_object_or_404(UserAccount, user=request.user)
Or, depending on your models, you might be able to follow the one-to-one field backwards, for example:
instance = request.user.useraccount
I'm relatively new to django and building my first app.
Tried searching through the site, but for the life of me cannot find the relevant information needed.
I'm looking to have a confirmation email sent to the entered email address on a contact form. I've seen examples of sending to a chosen address, or to user, but I can't seem to work out how to send mail to the email entered on the form.
Any help is greatly appreciated!
models.py:
from django.db import models
class Quote(models.Model):
name = models.CharField(max_length=200, blank=False, null=False, verbose_name="your name")
email = models.EmailField(max_length=255, blank=False, null=False)
created_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.name
forms.py:
class QuoteForm(forms.ModelForm):
class Meta:
model = Quote
views.py:
class QuoteView(CreateView):
model = Quote
form_class = QuoteForm
template_name = "quote/quote.html"
success_url = "/quote/success/"
def form_valid(self, form):
super(QuoteView,self).form_valid(form)
return HttpResponseRedirect(self.get_success_url())
class QuoteSuccessView(TemplateView):
template_name = "quote/quote-complete.html"
You can access validated form data (coerced to the fields respective types) via the cleaned_data attribute as shown in the form docs
https://docs.djangoproject.com/en/dev/topics/forms/#processing-the-data-from-a-form
from django.core.mail import send_mail
def form_valid(self, form):
super(QuoteView,self).form_valid(form)
send_mail("Foo", "bar", 'from#example.com', [form.cleaned_data['email']])
return HttpResponseRedirect(self.get_success_url())