im setting the request user in templates using this code, and this code work really good.
{% if user.is_authenticated %}
{{ user.name }}
{% else %}
xxxxxxx
{% endif %}
tables:
user(model.Models):
id
...
Profile(model.Models):
user onetoone(user)
name
last name
my user table have a onetoone relation with Profile and i want to know if i can get a field of Profile table using the request.user or other way to make it, thanks for all.
If I rightly understand you are using built-in user model. So we can write:
models.py
from django.contrib.auth.models import User
class Profile(models.Model):
***user*** = models.OneToOneField(User)
name = models.CharField(max_length=50)
User.*profile* = property(lambda u: Profile.objects.get_or_create(***user***=u)[0])
views.py
def profile(request):
user = request.user
profile = user.*profile*
after you can use profile fields like profile.name or other
Related
I have a model with user as 1 field (Foreign Key) and one other field skill_group. I need to make sure the user does not add duplicate skill groups so I added a UniqueConstraint. This is working as the system errors out with IntegrityError at /skillgroup/create/
duplicate key value violates unique constraint "unique_skillgroup" - How do I catch this exception and notify user if duplicate; otherwise save it?
New to Django/Python/Postgres and I thought I could handle it by overriding the save() function, but there is no access to user which is part of the check and I have read this should not be handled here. Is there a try/save catch/message I should be employing? I have tried a few things with no luck. I have seen similar questions on here, but they have not helped. Any help is appreciated.
models.py
class SkillGroup(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
skill_group = models.CharField(max_length=35)
sequence = models.IntegerField(default=999)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'skill_group'], name='unique_skillgroup'),
]
def __str__(self):
return self.skill_group
def get_absolute_url(self):
return reverse('skillgroup-list')
views.py
class SkillGroupCreateView(LoginRequiredMixin, CreateView):
model = SkillGroup
fields = ['skill_group']
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.sequence = SkillGroup.objects.filter(user=self.request.user).order_by('sequence').last().sequence + 1
return super().form_valid(form)
skillgroup_form.html
{% extends "recruiter/baseskills.html" %}
{% load crispy_forms_tags %}
{% block content%}
<div class="content-section">
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Skill Group</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Add Skill Group</button>
</div>
</form>
</div>
{% endblock content%}
I want to either catch the exception and save the record if not a duplicate or put message on screen saying "Skill Group already exists" and leave user on create page. Also, I could remove the UniqueConstraint and handle with code if that is the best solution.
You are inadvertently bypassing Django's form validation here and then trying to save invalid input to the database, which is why Django is feeding back an ugly IntegrityError from the database instead of handling the error gracefully.
If you submit a duplicate User and SkillGroup in your form, your CreateView will helpfully return the error message back into your form template:
"Skill group with this User and Skill group already exists."
But it can only do this if you include a User field in your form. I assume you have excluded User to keep the form template tidy, but that prevents Django's form validation from checking if the combination already exists.
To get around this, add User to your form field as a hidden input. I don't think that's possible using CreateView's behind-the-scenes magic, so you'll need to create a SkillGroupForm to handle that.
# forms.py
from django import forms
from .models import SkillGroup
class SkillGroupForm(forms.ModelForm):
class Meta:
model = SkillGroup
fields = ('user', 'skill_group')
widgets = {
'user': forms.HiddenInput,
}
# views.py
from .forms import SkillGroupForm
class SkillGroupCreateView(LoginRequiredMixin, CreateView):
model = SkillGroup
form_class = SkillGroupForm
def get_initial(self):
return {'user': self.request.user}
def form_valid(self, form):
form.instance.sequence = SkillGroup.objects.filter(user=self.request.user).order_by('sequence').last().sequence + 1
return super().form_valid(form)
The get_initial method passes the request.user as an initial value into the hidden form field, so no user input is needed.
Try to validate skill_group field inside your form class. Define clean_skill_group method like in docs.There you can get queryset of all SkillGroup objects related to your User (like here) and then compare skills. But you need to push somehow your User object or user_id (to get then User object) to the form before form.is_valid() will be called (or call one more time form.is_valid() after). Then show form errors in your html template.
class YourForm(forms.ModelForm):
....some fields, Meta....
def clean_skill_group(self):
your_user_object = ....
previously_created_skills = your_user_object.skill_group_set.all()
skill_input = self.cleaned_data["skill_group"]
if skill_input in previously_created_skills:
raise forms.ValidationError(("This skill group is already exist"), code="invalid") # I suppose you are using model form
Intro: I have a 3 models user, post, group. User is able to make posts however each post has to belong to a group. Users have to choose from the existing groups for their posts. Users cannot add, delete, update group's.
Furthermore:
Users can become a member of groups and when they click on a certain group. They see all the posts in that group.
What I want When Users come on the home page they see posts that were added since the last time they logged in
My Models
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
group = models.ForeignKey(Group, related_name='posts')
title = models.CharField(max_length=250, unique=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now=True)
My Views
class Homepage(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
context['object_list'] = Group.objects.filter(members=self.request.user)
#What am I doing wrong in the below code
new_posts = Post.objects.filter(created_at__gt=self.request.user.last_login).count()
context['new_posts'] = new_posts
else:
context['object_list'] = Group.objects.all()
return context
In my templates I have
<div class="list-group">
{% for group in object_list %}
{% if not new_posts %}
{{group.post.count}}
{% else %}
{{new_posts}}
{% endif %}
{% endfor %}
</div>
The Issue: Only the users who are already signed in and refresh their page see the new posts example:4 new, 3new etc... If a user signs in fresh after a new posts are created in a group. He does not see the 4 new, 3new . Instead it shows him just the number of posts in the group. not the new posts since he logged in. Why is this happening?
Well, its normal behavior because last_login stores datetime at which the user last logged in. When the user fresh logs in into the system, it stores the current time (timezone.now). So if a post is created before he is logged in, then it will not appear into the new_posts. So if you want to store the previous login/logout time, then you need to store it somewhere else(or make a new model field). You can try like this using user logged out signal:
from django.contrib.auth.signals import user_logged_out
def do_stuff(sender, user, request, **kwargs):
user.logout_time = timezone.now()
user.save()
user_logged_out.connect(do_stuff) # Hook an a method to user_logged_out signal to update logout time
And use it in the View like this:
last_post = Post.objects.last()
if last_post.created_at < request.user.last_login:
new_posts = Post.objects.filter(created_at__gt=self.request.user.logout_time).count()
context['new_posts'] = new_posts
Update
In this example, it is required to have a field to store logout time. If you have a CustomUser then you can directly create it in the CustomUser Model. Else, you can add it like this:
class Profile(models.Model):
user = models.OneToOneField(User)
logout_time = models.DateTimeField(default=timezone.now)
and store logout time like this:
user.profile.logout_time = timezone.now()
and filter New Posts Like this:
new_posts = Post.objects.filter(created_at__gt=self.request.user.profile.logout_time)
Models:
class Instructional_Cycle(models.Model):
date_started = models.DateField()
date_finished = models.DateField()
standard_tested = models.OneToOneField(Standard, on_delete=models.CASCADE)
class Standard(models.Model):
subject = models.CharField(max_length=14, choices=subjects)
grade_level = models.IntegerField(choices=gradeLevels)
descriptor = models.CharField(max_length=15)
description = models.TextField()
essential_status = models.BooleanField(default=False)
View:
class CycleCreateView(CreateView):
model = Instructional_Cycle
template_name = 'cycle_new.html'
fields = '__all__'
success_url = reverse_lazy('student_progress:cycles')
Template:
<!-- student_progress/cycle_new.html -->
{% extends 'base.html' %}
{% block content %}
<h1>Add a new instructional cycle:</h1>
<form action="{% url 'student_progress:cycle_new' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">add cycle</button>
</form>
{% endblock content %}
The problem I'm having with this form is that the dropdown to select Instructional_Cycle.standard_tested has literally 1000 records from Standard. There's no way that the user can scroll through all of those and find the one record they want.
What I need is some way to click a link and filter the dropdown list by subject or grade_level and/or a search box, similar to what's achieved on the admin side by creating a custom admin model in admin.py like so:
class StandardAdmin(admin.ModelAdmin):
list_display = ('descriptor', 'description', 'essential_status')
list_filter = ('subject', 'grade_level', 'essential_status')
search_fields = ('descriptor',)
inlines = [MilestoneInLine]
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
try:
search_term_as_int = int(search_term)
except ValueError:
pass
else:
queryset |= self.model.objects.filter(age=search_term_as_int)
return queryset, use_distinct
Please "dumb it down" for this newbie. I just finished working through Django for Beginners, and my conceptual model of how this all fits together is still full of holes. Please assume that I know hardly anything. Thanks!
That amount of reactive work on one page will require you to be comfortable with Javascript, Ajax, etc. If that is the case, there are a number of approaches you could take that let you refresh the form with the desired options.
Alternatively, you could ask the user for the necessary data one step earlier in the process and let Django build the correct form for you in the first place by overriding the form's default queryset.
You should look into using something like django-ajax-select. https://github.com/crucialfelix/django-ajax-selects
I have a model Sachbearbeiter which extends my User model.
from django.contrib.auth.models import User
class Sachbearbeiter(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
verein = models.ForeignKey(Verein)
Now I need CreateView form to create a new Sachbearbeiter instance that is immediately linked to a new User instance.
The form should have the following fields:
First Name: [textfield]
Last Name: [textfield]
Email Address: [textfield]
Verein: [Dropdown that lists `Vereine`]
What I tried is this:
class SachbearbeiterForm(ModelForm):
class Meta:
model = Sachbearbeiter
fields = '__all__' # Go into detail once it's working
class UserForm(ModelForm):
class Meta:
model = User
fields = '__all__' # Go into detail once it's working
SachbearbeiterFormSet = inlineformset_factory(User, Sachbearbeiter, fields='__all__')
class SachbearbeiterCreate(CreateView):
form_class = SachbearbeiterFormSet
But this gives me a form that looks like this:
Verein: [Dropdown that lists `Vereine`]
Delete? [checkbox]
Is this even possible, or do I have to create a custom form for that?
This is not the solution I was looking for, but works well. Because of that, I won't mark it as an answer, though.
I had to create a custom view and was able to simply use both forms inside it. (In the code example I omitted the unimportant stuff.
In the ModelForms I only changed the fields attribute to have only the fields I want to show.
def my_view(request):
...
if request.method == 'POST':
if user_form.is_valid() and sachbearbeiter_form.is_valid():
user = user_form.save(commit=False)
user.username = user.email
user.save()
sachbearbeiter = sachbearbeiter_form.save(commit=False)
sachbearbeiter.user = user
sachbearbeiter.save()
...
return redirect('sachbearbeiter_create_or_edit', username=user.username)
The template looks something like that:
{% if user_form.errors or sachbearbeiter_form.errors %}
<div class="alert alert-danger" role="alert">
Please correct the errors indicated by red color.
</div>
{% endif %}
<form method="post" action="#">
{{ user_form.as_p }}
{{ sachbearbeiter_form.as_p }}
</form>
This is my forms.py:
class RegistrationForm(forms.Form):
PET_CHOICES = (
('none','---'), ('dog', 'Dog'),
)
username = forms.CharField()
email = forms.EmailField()
password1 = forms.CharField()
birthdate = forms.DateField(widget=extras.SelectDateWidget)
pet = forms.ChoiceField(choices=PET_CHOICES)
This is my models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserExtended(models.Model):
user = models.ForeignKey(User)
pet = models.CharField(max_length=40)
age = models.PositiveSmallIntegerField()
As you can see, this model extends the default Django User model. Now, this is my view which registers users:
def registerView(request):
form = RegistrationForm()
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
user.userextended_set.pet=form.cleaned_data['pet']
user.userextended_set.age=form.cleaned_data['birthdate']
return HttpResponseRedirect('/')
I'm wondering, is this part of my code correct?
user.userextended_set.pet=form.cleaned_data['pet']
user.userextended_set.age=form.cleaned_data['birthdate']
With the two lines above, I am trying to add to the UserExtended model which has a ForiegnKey with User. I think I am doing something wrong because after the user is created and I authenticate and log the user in using the functions
authenticate()
and
login()
provided by django, and if I place this in my template which Django redirects to after logging in
<p>your pet is {{ user.userextended_set.pet }}</p>
it does not display another for
{{ user.userextended_set.pet }}
. It just says:
your pet is
. Any idea why it does not display anything? {{ user.userextended_set.pet }}
userextended_set is actually a manager, not a UserExtended object, because you have a ForeignKey in UserExtended, which indicates that a User could have multiple UserExtended objects. This probably isn't what you want — you probably want to use a OneToOneField instead.
The problem here is,
user.userextended_set returns a RelatedManager type. So, you cannot do a simple .pet would not work.
Now, looking at your models, i am assuming you intend UserExtended to a OneToOneField, rather than a ForeignKey. As long as you can manage the (non) creation of duplicates, you are just fine.
Now, coming back to your issue:
You can do
user_extended = user.userextended_set.all()[0]
#Or if you are sure it is a unique relationship,
#user_extended = user.userextended_set.get()
#Note - this would raise error if MultipleObjectsReturned, or DoesNotExist
user_extended.pet = form.cleaned_data['pet']
user_extended.age = form.cleaned_data['age']
Also, in the template,
{{ user.userextended_set.0.pet }}
Or even this:
{% for u_e in user.userextended_set.all %}
{{u_e.pet}}
{% empty %}
Not found
{% endfor %}