I have a list of users or users/roles type.
I want to be able to click a link to Update their respective profiles users.
When I perform click in Profile user, I should be able to edit the associated data to respective user according to profile that handle each of them. Each data profile is the simple page or template and manage for the same UpdateView class based view.
In detail my scenario is the following:
I have the User (inherit from AbstractBaseUser) model and here I manage the account data of the all users, this mean all data in common to all users roles or types which I want manage in my application such as:
user student
user professor
user executive
This users/roles type have their own model in where I define the respective fields to each of them. So, I have too, StudentProfile ProfessorProfile and ExecutiveProfile models, of this way:
My User model is:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True)
slug = models.SlugField(
max_length=100,
blank=True
)
is_student = models.BooleanField(
default=False,
verbose_name='Student',
help_text='Student profile'
)
is_professor = models.BooleanField(
default=False,
verbose_name='Professor',
help_text='Professor profile'
)
is_executive = models.BooleanField(
default=False,
verbose_name='Executive',
help_text='Executive profile',
)
other fields ...
def get_student_profile(self):
student_profile = None
if hasattr(self, 'studentprofile'):
student_profile = self.studentprofile
return student_profile
def get_professor_profile(self):
professor_profile = None
if hasattr(self, 'professorprofile'):
professor_profile = self.professorprofile
return professor_profile
def get_executive_profile(self):
executive_profile = None
if hasattr(self, 'executiveprofile'):
executive_profile = self.executiveprofile
return executive_profile
def save(self, *args, **kwargs):
user = super(User,self).save(*args,**kwargs)
# Creating an user with student profile
if self.is_student and not StudentProfile.objects.filter(user=self).exists():
student_profile = StudentProfile(user = self)
student_slug = self.username
student_profile.slug = student_slug
student_profile.save()
# Creating an user with professor profile
elif self.is_professor and not ProfessorProfile.objects.filter(user=self).exists():
professor_profile = ProfessorProfile(user=self)
professor_slug = self.username
professor_profile.slug = professor_slug
professor_profile.save()
# Creating an user with executive profile
elif self.is_executive and not ExecutiveProfile.objects.filter(user=self).exists():
executive_profile = ExecutiveProfile(user = self)
executive_slug = self.username
executive_profile.slug = executive_slug
executive_profile.save()
# I have this signal to get the username and assign to slug field
#receiver(post_save, sender=User)
def post_save_user(sender, instance, **kwargs):
slug = slugify(instance.username)
User.objects.filter(pk=instance.pk).update(slug=slug)
The idea behind of these schema is that when I create and user with is_student field checked the StudentProfile model is used for complete their data.
When I create and user with is_professor field checked the ProfessorProfile model is used for complete their data.
When I create and user with is_executive field checked the ExecutiveProfile model is used for complete their data.
The models for each Profile (Student, Professor and Executive) are such as follow:
class StudentProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
slug = models.SlugField(
max_length=100,
blank=True
)
origin_education_school = models.CharField(
_("origin education institute"), max_length=128
)
current_education_school = models.CharField(
_("current education institute"), max_length=128
)
extra_occupation = models.CharField(
_("extra occupation"), max_length=128
)
class ProfessorProfile(models.Model):
CATHEDRAL_PROFESSOR = 'CATHEDRAL'
RESEARCH_PROFESSOR = 'RESEARCH'
INSTITUTIONAL_DIRECTIVE = 'DIRECTIVE'
OCCUPATION_CHOICES = (
(CATHEDRAL_PROFESSOR, 'Cathedral Professor'),
(RESEARCH_PROFESSOR, 'Research Professor'),
(INSTITUTIONAL_DIRECTIVE, 'Institutional Directive'),
)
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
slug = models.SlugField(
max_length=100,
blank=True
)
occupation = models.CharField(
max_length=255,
blank = False,
)
class ExecutiveProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
slug = models.SlugField(
max_length=100,
blank=True
)
occupation = models.CharField(
max_length=255,
blank = False,
)
enterprise_name = models.CharField(
max_length=255,
blank = False,
)
I have the form to each profile update data of this way in my forms.py:
class UserUpdateForm(forms.ModelForm):
class Meta:
widgets = {
'gender':forms.RadioSelect,
}
fields = ("username", "email", "is_student",
"is_professor", "is_executive",)
model = get_user_model() #My model User
class StudentProfileForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = ('origin_education_school', 'current_education_school',
'extra_occupation')
class ProfessorProfileForm(forms.ModelForm):
class Meta:
model = ProfessorProfile
fields = ('occupation',)
class ExecutiveProfileForm(forms.ModelForm):
class Meta:
model = ExecutiveProfile
fields = ('occupation', 'enterprise_name', 'culturals_arthistic',
'ecological')
In my class based view AccountSettingsUpdateView I update the data related to the model Users, this mean the Account Data
class AccountSettingsUpdateView(LoginRequiredMixin, UpdateView):
model = get_user_model()
form_class = forms.UserUpdateForm
# success_url = reverse_lazy('dashboard')
context_object_name = 'preferences'
def get_context_data(self, **kwargs):
context = super(AccountSettingsUpdateView, self).get_context_data(**kwargs)
user = self.request.user
if user.is_student:
profile = user.get_student_profile()
context.update({'userprofile': profile})
elif user.is_professor:
profile = user.get_professor_profile()
context.update({'userprofile': profile})
elif user.is_executive:
profile = user.get_executive_profile()
context.update({'userprofile': profile})
return context
The url for the below view is this
url(r"^preferences/(?P<slug>[\w\-]+)/$",
views.AccountSettingsUpdateView.as_view(),
name='preferences'
),
This view AccountSettingsUpdateView works OK.
[02/Apr/2017 23:51:17] "GET /accounts/preferences/luisa/ HTTP/1.1" 200 18092
And, in my other view and only in this view I am updating the data related to the profile of each one of these users. this mean the profiles described above:
class AccountProfilesView(LoginRequiredMixin, UpdateView):
# When I ask for user with Student Profile
model = StudentProfile
form_class = forms.StudentProfileForm
# sending the form to ProfessorProfile
second_form_class = forms.ProfessorProfileForm
# sending the form to ExecutiveProfile
third_form_class = forms.ExecutiveProfileForm
success_url = reverse_lazy('dashboard')
template_name = 'accounts/student_form.html'
def get_context_data(self, **kwargs):
context = super(AccountProfilesView, self).get_context_data(**kwargs)
user = self.request.user
if 'form' not in context:
context['form'] = self.form_class(self.request.GET,
instance=user)
if 'form2' not in context:
context['form2'] = self.second_form_class(self.request.GET,
instance=user)
'''
if 'form3' not in context:
context['form3'] = self.third_form_class(self.request.GET,
instance=user)
'''
if user.is_student:
profile = user.get_student_profile()
context.update({'userprofile': profile})
elif user.is_professor:
profile = user.get_professor_profile()
context.update({'userprofile': profile})
elif user.is_executive:
profile = user.get_executive_profile()
context.update({'userprofile': profile})
return context
And the url to this AccountProfilesView view is this
url(r"^profile/(?P<slug>[\w\-]+)/$",
views.AccountProfilesView.as_view(
model=ProfessorProfile),
name='profile'
),
Note that in the url i am passing the ProfessorProfile model like parameter, despite that in the view AccountProfilesView body, I am defining the StudentProfile model, but what happen is that in the url the model = ProfessorProfile override to the model = StundentProfile which came from the view.
In this moment if I am use the luisa user with profile StudentProfile and I going to the url http://localhost:8000/accounts/profile/luisa/
the url is not found:
[03/Apr/2017 01:20:25] "GET /accounts/profile/luisa/ HTTP/1.1" 404 1771
Not Found: /accounts/profile/luisa/
But If I remove the attribute model=ProfessorProfile that I am passing like parameter in the URL, this mean that my url stay so:
url(r"^profile/(?P<slug>[\w\-]+)/$", views.AccountProfilesView.as_view(), name='profile')
The url http://localhost:8000/accounts/profile/luisa/ is OK
[03/Apr/2017 01:28:47] "GET /accounts/profile/luisa/ HTTP/1.1" 200 4469
This happen because in the view persist the model=StudentProfile attribute.
Until this point, if I am use an user with ProfessorProfile named david and I am going to their profile URL, this is not found
Not Found: /accounts/profile/david/
[03/Apr/2017 01:30:19] "GET /accounts/profile/david/ HTTP/1.1" 404 1769
But I add again the attribute model=ProfessorProfile that I am passing like parameter in the URL, such as mentioned above, the url of david profile ProfessorProfile is OK.
[03/Apr/2017 01:33:11] "GET /accounts/profile/david/ HTTP/1.1" 200 4171
The same inconvenient I have with the ExecutiveProfile user type.
According to the previous behavioral, is of this way, which, I am defining the view to ask for the user type roles and render their respective form.
But the inconvenient is that in my view AccountProfilesView I cannot pass or indicate more than One model.
I am trying indicate one second model in my AccountProfilesView of this way:
class AccountProfilesView(LoginRequiredMixin, UpdateView):
model = StudentProfile
form_class = forms.StudentProfileForm
second_form_class = forms.ProfessorProfileForm
third_form_class = forms.ExecutiveProfileForm
#success_url = reverse_lazy('dashboard')
template_name = 'accounts/student_form.html'
def get_context_data(self, **kwargs):
context = super(AccountProfilesView, self).get_context_data(**kwargs)
# Indicate one second model
context['professor_profile'] = ProfessorProfile
But the result is the same
In summary my question is:
In the UpdateView class based view ...
How to can I work with multiple model (more precisely three models StudentProfile, ProfessorProfile and ExecutiveProfile) to can render their respective model forms with the order of access to each profile page user?
I want can perform this wuth any amount of ProfileUser that I have.
I unknown if my schema User and ProfileUser model are good of if there is some better alternative of address this challenge.
UPDATE
According to the answer of #Ma0 Collazos their solution works great.
In this moment the goal is can make a combination of the different profiles, and can render the forms of each profile. So, if an user have a is_professor and is_executive profile, can show in their profile view (AccountProfilesView) their forms respective, this mean that when I go to profile user, can I see the fields the forms professor and the fields the form executive
With the order to get this purpose, I add the scenario in where an user have the combination of profiles in my AccountProfilesView such as follow:
class AccountProfilesView(LoginRequiredMixin, UpdateView):
# All users can access this view
model = get_user_model()
template_name = 'accounts/profile_form.html'
fields = '__all__'
def get_context_data(self, **kwargs):
context = super(AccountProfilesView, self).get_context_data(**kwargs)
user = self.request.user
if not self.request.POST:
if user.is_student:
profile = user.get_student_profile()
context['userprofile'] = profile
context['form_student'] = forms.StudentProfileForm()
elif user.is_professor:
profile = user.get_professor_profile()
context['userprofile'] = profile
context['form_professor'] = forms.ProfessorProfileForm()
elif user.is_executive:
profile = user.get_executive_profile()
context['userprofile'] = profile
context['form_executive'] = forms.ExecutiveProfileForm()
elif user.is_student and user.is_professor and user.is_executive:
student_profile = user.get_student_profile()
professor_profile = user.get_professor_profile()
executive_profile = user.get_executive_profile()
context['student_profile'] = student_profile
context['professor_profile'] = professor_profile
context['executive_profile'] = executive_profile
context['form_student'] = forms.StudentProfileForm()
context['form_professor'] = forms.ProfessorProfileForm()
context['form_executive'] = forms.ExecutiveProfileForm()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
user = self.request.user
if user.is_student:
context['form_student'] = forms.StudentProfileForm(self.request.POST)
elif user.is_professor:
context['form_professor'] = forms.ProfessorProfileForm(self.request.POST)
elif user.is_executive:
context['form_executive'] = forms.ExecutiveProfileForm(self.request.POST)
elif user.is_student and user.is_professor and user.is_executive:
context['form_student'] = forms.StudentProfileForm(self.request.POST)
context['form_professor'] = forms.ProfessorProfileForm(self.request.POST)
context['form_executive'] = forms.ExecutiveProfileForm(self.request.POST)
return super(AccountProfilesView, self).post(request, *args, **kwargs)
def form_valid(self, form):
context = self.get_context_data(form=form)
user = self.request.user
user = form.save()
if user.is_student:
student = context['form_student'].save(commit=False)
student.user = user
student.save()
elif user.is_professor:
professor = context['form_professor'].save(commit=False)
professor.user = user
professor.save()
elif user.is_executive:
executive = context['form_executive'].save(commit=False)
executive.user = user
executive.save()
elif user.is_student and user.is_professor and user.is_executive:
student = context['form_student'].save(commit=False)
student.user = user
student.save()
professor = context['form_professor'].save(commit=False)
professor.user = user
professor.save()
executive = context['form_executive'].save(commit=False)
executive.user = user
executive.save()
return super(AccountProfilesView, self).form_valid(form)
And in my profile form template, I have the following small logic in which the render of the form to each profile of a separate way works, but when I ask if an user have the three profiles is_student, is_professor and is_executive such as is code section at the end of my template, and I going to profile page of this user, the three forms does not renderized:
<form method="POST">
{% csrf_token %}
{% if userprofile.user.is_student %}
{% bootstrap_form form_student %}
{% elif userprofile.user.is_professor %}
{% bootstrap_form form_professor %}
{% elif userprofile.user.is_executive %}
{% bootstrap_form form_executive %}
{% elif userprofile.user.is_student and
userprofile.user.is_professor and
userprofile.user.is_executive %}
{% bootstrap_form form_student %}
{% bootstrap_form form_professor %}
{% bootstrap_form form_executive %}
{% endif %}
<input type="submit" value="Save Changes" class="btn btn-default">
</form>
Why my three forms, is not possible show them in one form?
Make class AccountProfilesView to have an attribute model = get_user_model(), so all users can access this view.
In your get_context_data method define what form is going to be render and make sure to fill this forms with data entered in a POST method
# NoQA
if not self.request.POST:
if user.is_student:
context['form_student'] = forms.StudentProfileForm()
elif user.is_professor:
context['form_professor'] = forms.ProfessorProfileForm()
...
else:
if user.is_student:
context['form_student'] = forms.StudentProfileForm(self.request.POST)
elif user.is_professor:
context['form_professor'] = forms.ProfessorProfileForm(self.request.POST)
...
Then override the form_valid method to save the corresponding forms
def form_valid(self, form):
context = self.get_context_data(form=form)
user = form.save()
# NoQA
if user.is_student:
student = context['form_student'].save(commit=False)
student.user = user
student.save()
elif user.is_professor:
professor = context['form_professor'].save(commit=False)
professor.user = user
professor.save()
...
Answer based on this post
Related
I am in the middle of a project. I have extended the custom django user and modified it.
this is my user model:-
class User(AbstractUser):
name = models.CharField(max_length=200, null=True, blank=True)
usertype = models.CharField(choices = [('d','doctor'), ('p','patient')], max_length=1)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
def __str__(self):
return self.name
Also I have declared two seperate models named Patient and Doctors. My objective is to register the users in their respective models(Doctors or Patients) by checking the usertype.
Here are those models:-
class Patient(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='patient')
dob = models.DateField(null=True, blank=True)
contact = models.CharField(null=True, blank=True, max_length=100)
def __str__(self):
return self.user.name
class Doctor(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='doctor')
deg = models.TextField(null=True, blank=True)
def __str__(self):
return self.user.name
Now at the front end I want to apply the logic as every time a user is registered the user selects the usertype and based on that selection the Doctor or the Patient module is updated.
I have tried creating separate forms for that too.
Here are my forms :-
class MyUserCreation(UserCreationForm):
class Meta:
model = User
fields = ['name','username','usertype']
class DoctorCreation(ModelForm):
class Meta:
model = Doctor
fields = ['user','deg']
class PatientCreation(ModelForm):
class Meta:
model = Patient
fields = ['dob', 'contact','user']
The view handling this URL is :-
def registerUser(request):
page = 'general'
form = MyUserCreation()
if request.method == 'POST':
form = MyUserCreation(request.POST)
if form.is_valid:
user = form.save(commit=False)
user.save()
login(request, user)
return redirect('home')
else:
messages.error(request, 'Error occured')
if user.usertype == 'p':
page = 'patient'
form = PatientCreation()
form = PatientCreation(request.POST)
if form.is_valid:
form.save()
elif user.usertype== 'd':
page = 'doctor'
form = DoctorCreation()
form = DoctorCreation(request.POST)
if form.is_valid:
form.save()
context = {'form':form, 'page':page}
return render(request, 'rec/register_user.html', context )
The front end for this project is handled with very basic HTML.
Also, if possible I want the front end such that every time a user is registered and the usertype is selected(which is a dropdown menu) some more fields show up depending on the usertype selection by the user. If selected Doctor the additional fields respective to the Doctor module show up, and same for the patient module.
To keep it simple on the front end this solution works like:
Loads Page with User Form
Submit User Form
Uses value to Load Next form
Submit Next Form + Redirect
Notes:
Uses the Values POSTed to determine what form is being submitted
Uses Initial to set User for the 2nd Form
This current flow could be broken up into 3 view with their own distinct URLs
Django View
def registerUser(request):
form = None
if request.method == 'POST':
valid = False
if 'usertype' in request.POST:
# 1st form submit
form = MyUserCreation(request.POST)
if form.is_valid:
valid = True
user = form.save(commit=False)
user.save()
login(request, user)
# Get 2nd form for load
if user.usertype == 'p':
page = 'patient'
form = PatientCreation(initial={'user':user})
elif user.usertype== 'd':
page = 'doctor'
form = DoctorCreation(initial={'user':user})
else:
# 2nd form submit
if 'dob' in request.POST:
form = PatientCreation(request.POST)
if form.is_valid:
form.save()
valid = True
elif 'deg' in request.POST:
form = DoctorCreation(request.POST)
if form.is_valid:
form.save()
valid = True
if valid:
# form sequence done
return redirect('home')
if not valid:
# a form failed somewhere
print(form.errors)
messages.error(request, 'Error occured')
if form == None:
page = 'general'
form = MyUserCreation()
context = {'form':form, 'page':page}
return render(request, 'rec/register_user.html', context )
Basic Django HTML Form
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
Now you could make this a single page by making the Template more complex, with JQuery Hiding/Showing extra fields based on dropdown Value on Change, but I assumed this would be the route you wanted.
Edit
To make the field disabled you'd just edit the forms.py
Note: the form-control is just showing that you can also add classes + extra attributes if you need to
class PatientForm(forms.ModelForm):
class Meta:
model = RunRequest
fields = (
'user',
'dob',
'contact',
)
def __init__(self, *args, **kwargs):
super(PatientForm, self).__init__(*args, **kwargs)
self.fields['user'].widget.attrs={'class': 'form-control', 'disabled':True}
I am fairly new to Django and right now not very familiar with the concept of Django. I created a user board (from simpleisbetterthancomplex.com) and would like to implement a Moderation with a "is_staff" user.
For now, only the user which created a post / comment is able to edit the post. As a is_staff user, I also want be able to edit ALL posts / comments.
This is the edit button:
{{ post.message }}
{% if post.created_by == user %}
<div class="mt-3">
<a href="{% url 'edit_post' post.topic.board.pk post.topic.pk post.pk %}"
class="btn btn-primary btn-sm"
role="button">Edit</a>
</div>
{% endif %}
I thought I could so something like:
{{ post.message }}
{% if user.is_staff %}
<div class="mt-3">
<a href="{% url 'edit_post' post.topic.board.pk post.topic.pk post.pk %}"
class="btn btn-primary btn-sm"
role="button">Edit</a>
</div>
{% endif %}
Although I reach the correct hyperlink to edit the post, I receive a "Page not found" error.
What could be an approach to implement some Moderation to my forum?
It's hard to tell what's wrong with your code. It seems both code samples should display the same button.
Please note that in this situation you should likely use a single button and tag, by changing the if to a slightly more complicated {% if user.is_staff or post.created_by == user %}. This should have the added effect of eliminating all possible discrepancies between the two buttons.
If you just want the ability to edit/delete posts, then the simplest way would likely be to use the built-in django admin panel. If you used django startproject, then your app already has one! Try going to localhost:8000/admin (by default) to check it out.
https://docs.djangoproject.com/pl/2.1/ref/contrib/admin/
EDIT: I think I can see the problem. You filter your queryset in PostUpdateView by (created_by=self.request.user). This filter works differently when dealing with a different user, such as the moderator. Try changing the view to reflect this.
The issue is the URL returned by edit_post. This view only allows access to owners of the post, so no one else can access this view.
You would need to add to the view to allow users with is_staff = True to also access this view.
The problem is with the queryset definition filtering out models not created by the user. But you want to keep this, so that other users don't have access.
You could remove the get_queryset call and adjust the dispatch method to only allow the owner or staff members to view. But I suggest keeping this intact and add a new moderator update view and use django braces to sort out the permissions. Something like;
from braces.views import StaffuserRequiredMixin
class ModeratorUpdateView(LoginRequiredMixin,
StaffuserRequiredMixin,
UpdateView):
## Update Code here ##
The view of my board:
class BoardListView(ListView):
model = Board
context_object_name = 'boards'
template_name = 'home.html'
class TopicListView(ListView):
model = Topic
context_object_name = 'topics'
template_name = 'topics.html'
paginate_by = 5
def get_context_data(self, **kwargs):
kwargs['board'] = self.board
return super().get_context_data(**kwargs)
def get_queryset(self):
self.board = get_object_or_404(Board, pk=self.kwargs.get('pk'))
queryset = self.board.topics.order_by('-last_updated').annotate(replies=Count('posts') - 1)
return queryset
#login_required
def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save(commit=False)
topic.board = board
topic.starter = request.user # <- here
topic.save()
Post.objects.create(
message=form.cleaned_data.get('message'),
topic=topic,
created_by=request.user # <- and here
)
return redirect('topic_posts', pk=pk, topic_pk=topic.pk) # TODO: redirect to the created topic page
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'board': board, 'form': form})
def topic_posts(request, pk, topic_pk):
topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
topic.views += 1
topic.save()
return render(request, 'topic_posts.html', {'topic': topic})
#login_required
def reply_topic(request, pk, topic_pk):
topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.topic = topic
post.created_by = request.user
post.save()
topic.last_updated = timezone.now()
topic.save()
topic_url = reverse('topic_posts', kwargs={'pk': pk, 'topic_pk': topic_pk})
topic_post_url = '{url}?page={page}#{id}'.format(
url=topic_url,
id=post.pk,
page=topic.get_page_count()
)
return redirect(topic_post_url)
else:
form = PostForm()
return render(request, 'reply_topic.html', {'topic': topic, 'form': form})
#method_decorator(login_required, name='dispatch')
class PostUpdateView(UpdateView):
model = Post
fields = ('message', )
template_name = 'edit_post.html'
pk_url_kwarg = 'post_pk'
context_object_name = 'post'
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(created_by=self.request.user)
def form_valid(self, form):
post = form.save(commit=False)
post.updated_by = self.request.user
post.updated_at = timezone.now()
post.save()
return redirect('topic_posts', pk=post.topic.board.pk, topic_pk=post.topic.pk)
class PostListView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'topic_posts.html'
paginate_by = 20
def get_context_data(self, **kwargs):
session_key = 'viewed_topic_{}'.format(self.topic.pk)
if not self.request.session.get(session_key, False):
self.topic.views += 1
self.topic.save()
self.request.session[session_key] = True
kwargs['topic'] = self.topic
return super().get_context_data(**kwargs)
def get_queryset(self):
self.topic = get_object_or_404(Topic, board__pk=self.kwargs.get('pk'), pk=self.kwargs.get('topic_pk'))
queryset = self.topic.posts.order_by('created_at')
return queryset
And the models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import Truncator
import math
class Board(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=100)
def __str__(self):
return self.name
def get_posts_count(self):
return Post.objects.filter(topic__board=self).count()
def get_last_post(self):
return Post.objects.filter(topic__board=self).order_by('-created_at').first()
class Topic(models.Model):
subject = models.CharField(max_length=255)
last_updated = models.DateTimeField(auto_now_add=True)
board = models.ForeignKey(Board, on_delete=models.CASCADE, related_name='topics')
starter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='topics')
views = models.PositiveIntegerField(default=0) # <- here
def __str__(self):
return self.subject
def get_page_count(self):
count = self.posts.count()
pages = count / 20
return math.ceil(pages)
def has_many_pages(self, count=None):
if count is None:
count = self.get_page_count()
return count > 6
def get_page_range(self):
count = self.get_page_count()
if self.has_many_pages(count):
return range(1, 5)
return range(1, count + 1)
def get_last_ten_posts(self):
return self.posts.order_by('-created_at')[:10]
class Post(models.Model):
message = models.TextField(max_length=4000)
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
updated_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='+')
def __str__(self):
truncated_message = Truncator(self.message)
return truncated_message.chars(30)
I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .
I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .
I'm using django-registration for registration and login purpose. My Models and Forms.py are working fine. The problem is I want to store the currently logged user's id in the user field of the following Model:
MALE = 1
FEMALE = 2
SEX_CHOICES = (
(MALE,'Male'),
(FEMALE,'Female'),
)
class UserProfile(models.Model):
#user = models.IntegerField() # update : Changed to ForeignKey.
user = models.ForeignKey(User)
gender = models.IntegerField(choices = SEX_CHOICES,null=True, blank=True)
zip_code = models.CharField(max_length=100,null=True, blank=True)
language = models.ManyToManyField(Category)
My ModelForm:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
widgets = {'language': forms.CheckboxSelectMultiple}
Update 2: Here is my view:
def some_view(request):
if request.method == 'POST':
form = UserProfileForm(request.POST)
if form.is_valid():
form.save()
else:
form = UserProfileForm()
Update: I can get currently logged in user's using {{ user.id }} in template. But now How do i pass this id to user field?
Well, in you view you can access the currently logged user via request.user.
Make this user initial data in your Form :
#login_required
def my_view(request):
logged_user = request.user
initial = {'user': logged_user}
form = MyForm(request.POST or None, initial=initial)
# Here your logical code
# ...
return HttpResponse(...)
And if you are using a ModelForm:
#login_required
def my_view(request):
logged_user = request.user
form = MyUserProfileModelForm(
request.POST or None,\
instance=logged_user.get_profile())
# Here your logical code
# ...
return HttpResponse(...)
Note: get_profile() is a OneToOneField and may raise a DoesNotExist exception if your database is screwed (which may happen if you have tried different things here).