I have the following custom SignupForm (simplified, works perfectly without my_file):
class SignupForm(forms.Form):
home_phone = forms.CharField(validators=[phone_regex], max_length=15)
my_file = forms.FileField()
def signup(self, request, user):
new_user = ReqInfo(
user=user,
home_phone=self.cleaned_data['home_phone'],
my_file=my_file,
)
new_user.save()
In models.py:
class ReqInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True, null=True)
home_phone = models.CharField(
validators=[phone_regex], max_length=15)
my_file = models.FileField(upload_to='uploads/directory/', validators=[resume_file_validator], blank=True, null=True)
My issue:
When I add a a user in myurl/accounts/signup it tells me that the my_file field is Required, even though I select a file.
The signup.html template allauth uses did not have
enctype="multipart/form-data"
After adding it, it works like a charm.
Related
I'm using Django and I'm getting the error AttributeError at /admin/network/post/
'Post' object has no attribute 'user'
The strange thing is this error happens when I'm looking at the admin section, and clicking 'Posts.' I only have models for users and posts. Not sure how to fix this error because so far I've never gotten an error like this when clicking it in the admin section of the site: http://127.0.0.1:8000/admin/
I think the issue is in my model because the view for creating a post works totally fine.
models.py
class User(AbstractUser):
pass
class Post(models.Model):
text = models.TextField(max_length=500, blank=True, null=True)
username = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='author',
null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(
User, blank=True, related_name="liked_user")
def __str__(self):
return self.user.username
class Follow(models.Model):
target = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='followers')
follower = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='targets')
views.py
def make_post(request):
if request.method == "GET":
form_for_post = {'form': PostForm()}
return render(request, "network/make_post.html", form_for_post)
else:
form = PostForm(request.POST)
if form.is_valid():
text = form.cleaned_data['text']
new_post = Post.objects.create(
text=text,
username=request.user,
)
return render(request, "network/make_post.html", {
"new_post": new_post,
})
You defined the field that refs to a User in the Post model to be username, not user, although user should be a better idea.
You thus should implement the __str__ method as:
class Post(models.Model):
# …
username = models.ForeignKey('User', on_delete=models.CASCADE, related_name='author', null=True, blank=True)
# …
def __str__(self):
return self.username.username
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
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.
UPDATE: I created a github repo with a full site demonstration of the problem.
Maybe my description below isn't quite communicating what I'm trying to do.
The github repo is: https://github.com/theCodeJerk/m2m-through
I really appreciate any help you may offer.
The code below is stripped down to illustrate the issue. While there are things that you may want to say "why would you do this anyway", there is probably a reason in the larger context :)
Here is my view:
class SubmissionCreate(CreateView):
model = Submission
fields = '__all__'
template_name_suffix = '_create_form'
success_url = '/'
Here is the relevant models.py code:
def custom_filename(instance, filename):
author = instance.publishers[0]
return 'papers/{0}.pdf'.format(author.pseudonum)
class Submission(models.Model):
name = models.CharField(
max_length=200,
blank=False
)
upload = models.FileField(
blank=True,
upload_to=custom_filename
)
publishers = models.ManyToManyField(
'Publisher',
blank=False,
related_name='publisher_of',
through='SubmissionPublisher'
)
class Publisher(models.Model):
user = models.ForeignKey(
User, blank=False,
on_delete=models.CASCADE
)
pseudonym = models.CharField(
max_length=200,
blank=False
)
class SubmissionPublisher(models.Model):
publisher = models.ForeignKey(
'Publisher',
blank=False,
on_delete=models.CASCADE
)
submission = models.ForeignKey(
'Submission',
blank=False,
on_delete=models.CASCADE
)
The problem is in the custom_filename, because I need the first publisher from the instance to generate the filename. The Submission is not yet saved when the SubmissionPublisher needs it to be saved.
What would the best way to do this be. Hopefully I have made sense here.
Thanks for any help!
Probably you can try like this:
First, update your custom_filename method:
def custom_filename(instance, filename):
if instance:
authors = instance.publishers.all()
if authors.exists():
author = authors[0]
return 'papers/{0}.pdf'.format(author.pseudonum)
return filename
Here I have fixed few issues, for example in your code instances.publishers[0] won't work, because you need to use a queryset method(like all(), or filter() etc) to access Publisher instances.
Then, make upload field nullable. Because you can't create M2M relations without creating Submission instance, and you can't create Submission instance with upload not null, because it requires an image.
class Submission(models.Model):
name = models.CharField(
max_length=200,
blank=False
)
upload = models.FileField(
null=True, default=None,
blank=True,
upload_to=custom_filename
)
Then, create a Form and override the save method:
from django import forms
from .models import Submission
class SubmissionForm(forms.ModelForm):
class Meta:
model = Submission
fields = '__all__'
def save(self, commit=True):
uploaded_file = self.cleaned_data.pop('upload')
instance = super().save(commit=True)
instance.upload = uploaded_file
instance.save()
return instance
Here I am pulling out the value for upload and saving the instance first. Then putting the image later. This code will work because upload field is nullable in your Submission model.
Finally, use that form class in your SubmissionCreate view:
class SubmissionCreate(CreateView):
model = Submission
form_class = SubmissionForm
template_name_suffix = '_create_form'
success_url = '/'
I have 2 models in Django that are connected by a one to one relationship. For example:
class User(AbstractBaseUser):
username = models.CharField(max_length=15, unique=True)
email = models.EmailField(max_length=100, unique=True)
date_joined = models.DateTimeField(auto_now_add=True,
null=True)
bio = models.CharField(max_length=200, null=True)
avatar = models.CharField(max_length=200, null=True)
profile = models.OneToOneField(Settings, null=True)
class Profile(models.Model):
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
bio = models.CharField(max_length=200, null=True)
avatar = models.CharField(max_length=200, null=True)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True)
dob = models.DateField(max_length=8, null=True)
Every user may have Profile details, but some users may not have entered any information so they might not have any details provided yet (0 rows). What I'd like to do is create a settings page that either creates or updates the user profile details upon submission. Basically this is what I have so far:
class ProfileSettings(UpdateView):
template_name = 'oauth/profile-settings.html'
form_class = ProfileForm
model = Profile
def get_object(self, queryset=None):
return get_object_or_404(Profile, user=self.request.user)
With this type of view, the update works when a user has profile details, but when the user doesn't have profile details a 404 error is displayed instead. Is it possible to display the view to the user even though he doesn't have profile details and if so, could they create profile details for themselves and then can go back and update them?
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})