I have just finished a Django tutorial for an email sign up form and I am trying to tweak some parts of the code to match my needs (adding an additional field to the sign up form)
Based on the tutorial I created an email signup Model form:
class JoinForm(forms.ModelForm):
class Meta:
model = Join
fields = ["email","wunschrad"]
and I altered it a little bit by adding the wunschrad field
then I updated my model to include wunschrad and synced the database with south, all working out fine. below my model:
class Join(models.Model):
email = models.EmailField()
wunschrad = models.CharField(max_length=120)
friend = models.ForeignKey("self", related_name='referral', null=True, blank=True)
ref_id = models.CharField(max_length=120, default='ABC', unique=True)
ip_address = models.CharField(max_length=120, default='ABC')
timestamp = models.DateTimeField(auto_now_add = True, auto_now = False)
updated = models.DateTimeField(auto_now_add = False, auto_now = True)
def __unicode__(self):
return "%s" % (self.email)
class Meta:
unique_together = ("email", "ref_id",)
Where I ran into problems is how do I adopt my view to reflect this change. Here is what I did (my changes highlighted in comments)
def home(request):
try:
join_id = request.session['join_id_ref']
obj = Join.objects.get(id=join_id)
except:
obj = None
form = JoinForm(request.POST or None)
if form.is_valid():
new_join = form.save(commit=False)
email = form.cleaned_data['email']
wunschrad = form.cleaned_data['wunschrad'] #I ADDED THIS
new_join_old, created = Join.objects.get_or_create(email=email, wunschrad=wunschrad) # I ADDED WUNSCHRAD HERE
if created:
new_join_old.ref_id = get_ref_id()
if not obj == None:
new_join_old.friend = obj
new_join_old.ip_address = get_ip(request)
new_join_old.save()
return HttpResponseRedirect("/%s" %(new_join_old.ref_id))
context = {"form": form}
template = "home.html"
return render(request, template, context)
My question/ problem is the following: This code snippet works but if a user signs up with the same email address and chooses once the first option of wunschrad and the second time the second option (for clarification: wunschrad is a drop-down list with two options) Django saves two versions in the database, like shown here: Django Admin Screenshot
Does anyone have an idea how to alter the code to save it only once per user?
many many thanks in advance for the help!
So your problem here is by adding wunschrad to get_or_create you are telling Django to look for an entry in the database that has both of those values instead of just the email. Changing it to this should fix it:
new_join_old, created = Join.objects.get_or_create(email=email)
new_join_old.wunschrad = wunschrad
new_join_old.save()
This will make sure it only creates a new object if the email isn't in the database already.
As a side note if you want your email to be unique and raise an error if you try to save another entry with the same email add unique=True to models.EmailField() like:
models.EmailField(unique=True)
Related
Here is the scenario I am working on: I have django app that creates records which I call sessions:
blog.models.py
class Session(models.Model):
uid = models.CharField(max_length=50, blank=True)
cid = models.CharField(max_length=50, blank=True)
action_type = models.CharField(max_length=50, blank=True)
action_name = models.CharField(max_length=50, blank=True)
action_value = models.CharField(max_length=50, blank=True)
session_date = models.DateTimeField(auto_now_add=True)
client = models.CharField(max_length=50, blank=True)
I have a dashboard page to show charts and a database page to show the records as a table:
blog.urls.py
path('', auth_views.LoginView.as_view(template_name='users/login.html'), name='blog-home'),
path('<str:username>/dashboard/', views.dashboard and DashboardListView.as_view(), name='blog-dashboard'),
path('<str:username>/database/', views.database and SessionListView.as_view(), name='blog-database'),
So when you log in, my SessionListView.as_view() goes through the whole database and displays only those records where the Session.client == the url's 'username' value.
Example: when user: DummyCo logs in (www.website.com/DummyCo/database/) they see only Session records where the Session.client field is 'DummyCo.' This has worked out great so far.
But here is the problem: I now need to provide multiple logins to users to see the same dashboard and database page.
Example: jim#DummyCo.com and amy#DummyCo.com both need to see the DummyCo records, but if I provided them with their own logins then their username's in the url would not match and thus the DummyCo records would not show. I thought using the built-in django Groups would be a solution but that seems to only help with authentication and permissions on the backend. I also extended my user model with a Profile model:
users/models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, blank=True, null=True, default=None, on_delete=models.SET_DEFAULT)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
user_client = models.CharField(max_length=50, blank=True, null=True, default=None)
def __str__(self):
return f'{self.user.username} Profile'
I made the user_client model field to try and connect the Profile (and thus User) with the Session.client field: instead of <str:username>/database/ I thought i'd be able to use <str:client_user>/database/ and simply fill that field with 'DummyCo' on both Jim and Amy's profile to give them access to the records.
I read in a couple of places that the key to handling this problem is to switch the user model from one-to-one to many-to-one type early or before i build out the app. Unfortunately I have already put a ton of work into this project. I also read that I should look at the built-in User model as more of an account and less of a user. So is there a simple way to give multiple users access to one User/account?
Also, here is the views:
blog/views.py
class SessionListView(LoginRequiredMixin, ListView):
model = Session, Profile
template_name = 'blog/database.html'
context_object_name = 'sessions'
ordering = ['-session_date']
paginate_by = 25
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Session.objects.filter(client=user).order_by('-session_date')
def get_context_data(self, **kwargs):
user = get_object_or_404(User, username=self.kwargs.get('username'))
context = super().get_context_data(**kwargs)
context['distinct_campaigns'] = Session.objects.filter(client=user).values('cid').distinct().order_by('cid')
context['distinct_action_types'] = Session.objects.filter(client=user)\
.values('action_type')\
.distinct().order_by('action_type')
return context
# login_required()
def database(request):
context = {
'sessions': Session.objects.all()
}
return render(request, 'blog/database.html', context, {'title': 'Database'})
Okay I figured out a solution:
I thought I needed to do some trickery on the html file within the for loop showing my query set sessions but it turns out that can be adjusted in my views.py file. Before this update my views.py looked like this:
class SessionListView(LoginRequiredMixin, ListView):
model = Session, Profile
template_name = 'blog/database.html'
context_object_name = 'sessions'
ordering = ['-session_date']
paginate_by = 25
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Session.objects.filter(client=user).order_by('-session_date')
def get_context_data(self, **kwargs):
user = get_object_or_404(User, username=self.kwargs.get('username'))
context = super().get_context_data(**kwargs)
context['distinct_campaigns'] = Session.objects.filter(client=user).values('cid').distinct().order_by('cid')
context['distinct_action_types'] = Session.objects.filter(client=user)\
.values('action_type')\
.distinct().order_by('action_type')
return context
I realized the def get_queryset(self) was grabbing the logged-in username, then reviewing the full database and adding all records with the same session.client value as the value of the logged in user (i.e. DummyCo). So to make this work for a user like 'DummyCo_Sally', I changed the logic in that def like so:
class SessionListView(LoginRequiredMixin, ListView):
# gets the actual user (i.e. DummyCo_Sally)
user = get_object_or_404(User, username=self.kwargs.get('username'))
# turns user to a string
user_string = str(user)
# designates the _ as the separator
sep = '_'
# strips off _ and everything after it
stripped_user = user_string.split(sep, 1)[0]
# establishes the queryset as 'DummyCo' even though 'DummyCo_sally' is logged in
return Session.objects.filter(client=stripped_user).order_by('-session_date')
I doubt this method is the best way of handling multiple users seeing one umbrella data set, but it did the trick for me. This method also likely creates a security risk for applications that have public-facing user registration. But it did the trick for me.
I am very new to Django and try to build a little app for my family with some basic features, one of which is a blog app. In this now I want to store the author of a blog entry in the article model to save it in the database. In the end in the blog entry I want a header that displays the author and the datetime.
I found several approaches to that but some how none of them worked for me so far. The last thing I did now was to try and grab the username in the view with requests.user.get_username() and then populate the field in my form with this string as initial value. Somehow it just doesn't come through, I always get the "field is required" error message...
Here is my blog-create-view.py:
def blog_create_view(request):
user = request.user.get_username()
data = {'author': user}
form = BlogForm(request.POST, data)
if form.is_valid():
form.save()
return HttpResponseRedirect('/blog/blog-create/thanks')
else:
print(form.errors)
form = BlogForm()
context = {
'articleform' : form,
}
return render(request, "blog-create.html", context)
this is the form:
class BlogForm(forms.ModelForm):
title = forms.CharField(label='',
widget=forms.TextInput(attrs={"placeholder":"Titelfeld"}))
text = forms.CharField(
label='',
widget=forms.Textarea(
)
)
author = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = Article
fields = [
'title',
'text',
]
and this is the model:
class Article(models.Model):
title = models.CharField(blank=False,max_length=1000)
text = models.CharField(blank=False, default='Text', max_length=10000)
datetime = models.DateTimeField(auto_now=True)
featured = models.BooleanField(default='False')
author = models.CharField(max_length=1000)
Help is much appreciated! I am getting really frustrated trying to figure this out
if you want to add the author name as current logged in users username you can do it as follows. And remove author in the form
if form.is_valid():
instance = form.save(commit=False)
instance.author = request.user.username
instance.save()
Being said that, it will be better to change the author to foreignkey from django.contrib.auth.models import User and assign to the request.user rather than to the username, so that you can filter the articles by Article.objects.filter(author=request.user)
I am trying to insert django form data inside the UserProfile model in my app. I tried using the django shell and views.py but I keep getting this error.
Models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
section = models.CharField(max_length=255, blank=True)
year = models.IntegerField(null=True, blank=True)
course = models.CharField(max_length=255, blank=True)
qrcode = models.CharField(max_length=255, blank=True)
present = models.BooleanField(default=False)
def __str__(self):
return self.user.username
views.py
#staticmethod
def generate_qr(request):
if request.method == "POST":
form = MakeAttendance(request.POST)
if form.is_valid():
course = form.cleaned_data.get('courses')
section = form.cleaned_data.get('section')
year = form.cleaned_data.get('year')
profile = UserProfile.objects.get_or_create(user=request.user)
userobj = UserProfile(qrcode=unique_id)
userobj.save().filter(course=course, section=section, year=year)
return redirect('/users/dashboard')
This question has been answered many times here, but none of the solutions worked for me. I tried Creating a user profile with get_or_create method. I tried deleting my entire database and making migrations again. I manually tried to pass the user ID but nothing.
First create a user using user=User.objects.create_user(username=request.user, password='password'), then save it using user.save() and create profile using profile=UserProfile.objects.get_or_create(user=user). The reason this error occours is because the UserProfile looks for a user instance which you did not provide.
The problem is in these two line
userobj = UserProfile(qrcode=unique_id)
userobj.save().filter(course=course, section=section, year=year)
In the first line you created an instance of UserProfile with only qr_code
and in the next line you are trying to save it which will try to insert a new row in the database without the user.
in models.py you should create user object:
from django.conf import settings
User = settings.AUTH_USER_MODEL
before class creating
I have created a Edit Profile form in my Django app but it doesn't save in the database.
This is the profile model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', primary_key=True) #Each User is related to only one User Profile
city_search_text = models.CharField(blank=True, max_length=300)#user picks a city from autocomplete and in the view I get or create a City object
city = models.ForeignKey(City, blank=True, null=True, related_name='city') #Each User Profile must be related to one city.
prof_pic = models.ImageField(blank=True, upload_to='profile_pictures')
dob = models.DateField(blank=True, null=True)
def __str__(self):
return self.user.first_name
This is the form:
class EditProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('dob',)#I'm testing to update this field only
def save(self, commit=True):
profile = super(EditProfileForm, self).save(commit=False)
if commit:
profile.save()
return profile
This is the view:
def editprofile(request):
if request.method == 'POST':
edit_profile_form = EditProfileForm(request.POST, instance=request.user)
if edit_profile_form.is_valid():
profile = edit_profile_form.save(commit=False)
profile.save()
if 'next' in request.GET:
return redirect(request.GET['next'])
else:
print (profile_form.errors)
else:
edit_profile_form = EditProfileForm(instance=request.user.profile)
return render(request, 'excurj/editprofile.html', {'edit_profile_form':edit_profile_form,})
After I submit the form it forwards me to index page okay but the values remain the same in the user's profile.
Seems like
if edit_profile_form.is_valid():
isn't getting called at all, and your data is not saved. That means your form has invalid data, and you should check for form errors to detect those.
Also, you are trying to print form errors if the request isn't POST, which makes no sense and won't help you printing form errors. Try using this way;
if edit_profile_form.is_valid():
profile = edit_profile_form.save(commit=False)
profile.save()
else:
print (profile_form.errors)
And check your form for errors.
I figured it out eventually. In the view I should have passed an instance of the profile not the User object. So it needs to be like this:
edit_profile_form = EditProfileForm(request.POST, instance=request.user.*profile*)
I have a model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(max_length=30, default='anon')
last_name = models.CharField(max_length=30, default='anon')
interest = models.CharField(max_length=30, default='nothing')
def __str__(self):
return 'Username:%s' % self.user.username
class Post(models.Model):
title = models.TextField(default='No title')
text = models.TextField(max_length=220)
vote = models.IntegerField(default=0)
user_post = models.ForeignKey(User, related_name='post')
class Comments(models.Model):
name = models.TextField(default='Anon', null=True)
comment = models.TextField(max_length=2000, null=True)
post = models.ForeignKey(Post)
def __str__(self):
return 'comment:%s' % self.comment
In the post you can see I'm linking a post with the User. I read that you can access a users comments this way by using user.post_set.all(), so I gave it a try. When I attempt to migrate the change(adding the foriegn key in Post), I get an error.
django.db.utils.IntegrityError: NOT NULL constraint failed:
boardapp_post__new.user_post_id
I notice the post__new, so here is my view named new, which creates a post.
def new(request):
if request.method == 'POST':
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''),vote=0
)
return HttpResponseRedirect(reverse('view_post', args=(post.id,)))
return render(request, 'new.html')
I'm new to creating users in django and am confused as to where not null consraint is failing. Thanks
When you migrate to your new database format, there should be a default User for the user_posts that already exist.
Django will try to fill this value for you in existing user_posts, but it doesn't know which value to chose, since null is not allowed in your current model.
Thus: you need to tell Django either 1) not to worry about Posts without a user (null = True) or 2) supply a default User, which is a bit harder (probably would require a function call that creates some dummy User on the fly).
So the easiest solution is to alter your Post model and change user_post:
user_post = models.ForeignKey(User, related_name='post', null=True)
It is failing because your model field is
user_post = models.ForeignKey(User, related_name='post')
it means every post will be assigned to a user(not null field).
From your view, you are not assigning any user to the post, so change your code to
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''), vote=0, user_post=request.user
)