django class based view many to many field display selected values - python

I have a form created with Update View class based view. It displays every value in input type fields from database, except values of many to many field.
What should i insert into view or maybe model so user will see what options they have slected (like in built-in admin module).
This is my view:
class StudentUpdateView(LoginRequiredMixin, UpdateView):
form_class = StudentForm
model = Student
template_name = 'forms/modify.html'
def get_context_data(self, **kwargs):
context = super(StudentUpdateView, self).get_context_data(**kwargs)
context['year'] = Setting.objects.get(id=2)
return context
def form_valid(self, form):
if self.request.is_ajax():
self.object = form.save()
return HttpResponse(json.dumps("success"),
mimetype="application/json")
return super(StudentUpdateView, self).form_valid(form)
def form_invalid(self, form):
if self.request.is_ajax():
return HttpResponseBadRequest(json.dumps(form.errors),
mimetype="application/json")
return super(StudentUpdateView, self).form_invalid(form)
EDIT:
This is my model used, as Model Form
And StudentForm does not exist
class Student (models.Model):
name = models.CharField(u'ImiÄ™', max_length=40)
surname = models.CharField(max_length=80)
email = models.EmailField(blank=True)
subjects = models.ManyToManyField(Subjects, blank=True)
programs = models.ManyToManyField(Programs, editable=False)
history = models.CharField(max_length=50, editable=False)
added = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s %s'(self.name, self.surname)
class StudentForm(ModelForm):
class Meta:
model = Student

Related

How display categories in forms only for user who created this?

I am creating app for control transaction (expense, income, budget). I want each user to be able to create their own spending categories and their own expenses. All expenses and categories created by a user are to be visible only to that user.
If user A creates the category "Food123" then user B cannot see it. He can create his own such category.
I've created two models - Category and Expense.
class Category(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="category")
class Expense(models.Model):
name = models.CharField(max_length=100)
amount = models.DecimalField(max_digits=8, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="expense")
I use a generic view when creating expenses and categories.
class ExpenseListView(LoginRequiredMixin, ListView):
model = Expense
context_object_name = 'expense'
template_name = 'expense/expense_list.html'
def get_queryset(self):
return self.request.user.expense.all()
class ExpenseCreateView(CreateView):
model = Expense
success_url = '/record/expense'
form_class = ExpenseForm
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
class CategoryCreateView(CreateView):
model = Category
success_url = '/record/expense'
form_class = CategoryForm
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
What is more, I've used forms.py.
class ExpenseForm(forms.ModelForm):
class Meta:
model = Expense
fields = ('name', 'amount', 'category')
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name',)
Unfortunetly, when user A create category "IT" it automatically goes to user B. When user B creates his expense he also sees this "IT" category that A created, not B.
How can I limit the display of categories in the form to only those created by a particular user?
The problem is because you're not filtering the model fields by your user in the ExpenseForm (like you are doing in the ExpenseListView.get_queryset).
To do it, you will need to change a bit your logic. you can try something like this:
# forms.py
class ExpenseForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
# limit the category field queryset
self.fields['category'] = forms.ModelChoiceField(
queryset=user.category.all())
)
class Meta:
model = Expense
fields = ('name', 'amount', 'category')
# views.py
class ExpenseCreateView(CreateView):
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
# inject the user in the form instantiation
kwargs['user'] = self.request.user
return kwargs

How to use objects specified by a manager instead of the default objects in a class based or function view?

My code:
models.py
class EmployeeManager(models.Manager):
def get_queryset(self):
return super().get_queryset().exclude(employed=False)
class NotEmployedEmployee(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(employed=False)
class Employee(models.Model):
objects = EmployeeManager()
not_employed = NotEmployedEmployees()
name = models.CharField(max_length=250)
employed = models.BooleanField(default=True, blank=True, null=True)
views.py
class EmployeeListView(ListView):
model = Employee
template_name = 'tmng/employee_list.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
resultset = EmployeeFilter(self.request.GET, queryset=self.get_queryset())
context['filter'] = resultset
return context
class EmployeeUpdateView(UpdateView):
template_name = 'tmng/employee_update.html'
model = Employee
fields = '__all__'
def form_valid(self, form):
self.name = form.cleaned_data.get('name')
return super().form_valid(form)
def get_success_url(self):
messages.success(self.request, f'Employee "{self.name}" changed!')
return '/'
For all my currently working employees my list and update view works fine.
But I also want a list/update-view for my not-employed employees so I can 'reactivate' them once they rejoin the company.
For the list view I found a semi-solution by using a function based view.
views.py
def not_employed_employee_list_view(request, *args, **kwargs):
template_path = 'tmng/employee_not_employed.html'
context = {'employees': Employee.not_employed.all()}
return render(request, template_path, context)
So what I'm looking for is a way to see list/update non employed employees. Is there a way to say to class based / functions views to use not the default employees but the 'non_employed' employees?
I did not create new templates, but just created a new class based list view
class EmployeeNotEmployedListView(EmployeeListView, ListView):
def get_queryset(self):
return Employee.not_employed.all()
And for the update view, I updated the default Employee update view
class EmployeeUpdateView(UpdateView):
template_name = 'tmng/employee_update.html'
model = Employee
fields = '__all__'
def get_queryset(self):
return Employee.objects.all() | Employee.not_employed.all()
def form_valid(self, form):
self.name = form.cleaned_data.get('name')
return super().form_valid(form)
def get_success_url(self):
messages.success(self.request, f'Employee "{self.name}" changed!')
return '/'

Django Class based UpdateView with Form for Multiple Uploaded Files

I have two questions in regards to the problem I am currently facing:
Is it best practice in django to overwrite the post method in the CreateView? If it isn't do you write a form _valid function on the CategoryFullForm or in the CreateView and how would it look? The CreateView currently works great, but want to make sure there isn't a better way to do this.
If this is best practice, how would you override the get function in the UpdateView so you would be able to edit the files that relate to the instance being uploaded and even add an additional File? Open to other ways to accomplish this task as well.
models.py
############ CATEGORY MODEL ################
class Category(models.Model):
category_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100, null=True, blank=True)
est_pr_sqft = models.FloatField(blank=True)
est_duration = models.IntegerField(blank=True)
preceding_job = models.CharField(max_length=100, blank=True)
category_notes = models.CharField(max_length=250, blank=True)
category_slug = models.SlugField(max_length=100, unique=True, null=False)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
date_posted = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('category-detail', kwargs={'slug': self.category_slug})
def save(self, *args, **kwargs):
if not self.category_slug:
self.category_slug = slugify(self.name)
return super().save(*args, **kwargs)
class CategoryFiles(models.Model):
category_id = models.ForeignKey(Category, on_delete=models.CASCADE)
document = models.FileField(upload_to="attachments",null=True,blank=True)
uploaded_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def delete(self, *args, **kwargs):
self.document.delete()
return super().delete(*args, **kwargs)
forms.py
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ['name', 'est_pr_sqft', 'est_duration', 'preceding_job', 'category_notes']
class CategoryFullForm(CategoryForm):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}),required=False)
class Meta(CategoryForm.Meta):
fields = CategoryForm.Meta.fields + ['files']
views.py
####### CATEGORY VIEWS ########
class CategoryListView(LoginRequiredMixin, ListView):
model = Category
template_name = 'ProjectManagementApp/PM-Category-List.html'
context_object_name = 'categories'
slug_field = 'project_slug'
success_url = reverse_lazy('Category-list')
class CategoryDetailView(LoginRequiredMixin, DetailView):
model = Category
template_name = 'ProjectManagementApp/PM-Category-Detail.html'
slug_field = 'category_slug'
def get_context_data(self, **kwargs):
context = super(CategoryDetailView, self).get_context_data(**kwargs)
context['files'] = CategoryFiles.objects.all()
#context['photos'] = CategoryPhotos.objects.all()
return context
class CategoryCreateView(LoginRequiredMixin, CreateView):
model = Category
form_class = CategoryFullForm
template_name = 'ProjectManagementApp/PM-Category-Create.html' # Replace with your template.
#success_url = reverse_lazy('category-detail')
slug_field = 'category_slug'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('files')
if form.is_valid():
author = request.user
name = form.cleaned_data['name']
est_pr_sqft = form.cleaned_data['est_pr_sqft']
est_duration = form.cleaned_data['est_duration']
preceding_job = form.cleaned_data['preceding_job']
category_notes = form.cleaned_data['category_notes']
category_obj = Category.objects.create(name=name,est_pr_sqft=est_pr_sqft,est_duration=est_duration,preceding_job=preceding_job,category_notes=category_notes,author=author)
for f in files:
CategoryFiles.objects.create(category_id=category_obj,document=f)
return HttpResponseRedirect(reverse_lazy('Category-list'))
else:
return self.form_invalid(form)
class CategoryUpdateView(LoginRequiredMixin, UpdateView): #UserPassesTestMixin - makes sure user who made entry is only one who can update it.
model = Category
form_class = CategoryFullForm
template_name = 'ProjectManagementApp/PM-Category-Create.html' # Replace with your template.
slug_field = 'category_slug'
success_url = reverse_lazy('Category-list')
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('files')
if form.is_valid():
author = request.user
name = form.cleaned_data['name']
est_pr_sqft = form.cleaned_data['est_pr_sqft']
est_duration = form.cleaned_data['est_duration']
preceding_job = form.cleaned_data['preceding_job']
category_notes = form.cleaned_data['category_notes']
category_obj = Category.objects.create(name=name,est_pr_sqft=est_pr_sqft,est_duration=est_duration,preceding_job=preceding_job,category_notes=category_notes,author=author)
for f in files:
CategoryFiles.objects.create(category_id=category_obj,document=f)
return HttpResponseRedirect(reverse_lazy('Category-list'))
else:
return self.form_invalid(form)
#
# #Doesnt allow you to update other users post
# #def test_func(self):
# # project = self.get_object()
# # if self.request.user == post.author:
# # return True
# # return False
#
#
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
class CategoryDeleteView(LoginRequiredMixin, DeleteView): #UserPassesTestMixin - makes sure user who made entry is only one who can update it.
model = Category
template_name = 'ProjectManagementApp/PM-Category-Delete.html'
slug_field = 'category_slug'
success_url = reverse_lazy('Category-list')
#Doesnt allow you to delete other users post
#def test_func(self):
# project = self.get_object()
# if self.request.user == post.author:
# return True
# return False
If the urls.py/admin.py are needed am happy to provide, but don't think they are needed, as well as any of the html files.

ValueError at /post/new/ Cannot assign "<SimpleLazyObject:<User: chetan>>": "Post.author" must be a "User" instance

I keep getting this error "ValueError at /post/new/
Cannot assign ">": "Post.author" must be a "User" instance."
class User(models.Model):
user = models.OneToOneField(User,on_delete = models.CASCADE)
def __str__(self):
return self.user.username
class Post(models.Model):
author = models.ForeignKey('User.User',related_name="posts",on_delete = models.CASCADE)
text = models.TextField()
created_at = models.DateTimeField(default = timezone.now)
updated_at = models.DateTimeField(blank = True,null =True)
def update(self):
updated_at = timezone.now()
self.save()
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class CreatePostView(LoginRequiredMixin, CreateView):
# form_class = forms.PostForm
fields = ['text',]
model = Post
def form_valid(self, form):
self.object = form.save(commit=False)
form.instance.author = self.request.user
self.object.save()
return super().form_valid(form)
Please help!
You here made two User objects, the User object from django.contrib.auth and one from the User app. In order to retrieve the related User object from the User app, you can use self.request.user.user (given you of course constructed that related object):
class CreatePostView(LoginRequiredMixin, CreateView):
# form_class = forms.PostForm
fields = ['text',]
model = Post
def form_valid(self, form):
self.object = form.save(commit=False)
form.instance.author = self.request.user.user
self.object.save()
return super().form_valid(form)
It is however very confusing to name two models the same. It might be better to just use Profile for example. In that case it would be form.instance.author = self.request.user.profile which is also more readable.

Edit another model after creating an instance

I am finding it difficult to achieve the following scenario:
In my app, a user creates a project, now to that project he can link a previous team that he created or create a new team.
A team can be part of many projects but a project is linked to only ONE team.
models.py:
class Team(models.Model):
team_name = models.CharField(max_length=100, default = '')
team_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
def __str__(self):
return self.team_name
class TeamMember(models.Model):
user = models.ForeignKey(MyUser)
team = models.ForeignKey(Team)
def __str__(self):
return self.user.first_name
class Project(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
def __str__(self):
return self.name
views.py:
class ProjectCreate(CreateView):
model = Project
fields = ['name']
template_name = 'project_form.html'
def form_valid(self, form):
form.instance.project_hr_admin = self.request.user
return super(ProjectCreate, self).form_valid(form)
class ProjectDetailView(generic.DetailView):
model = Project
template_name = 'project_details.html'
class TeamCreate(CreateView):
model = Team
fields = ['team_name']
template_name = 'team_form.html'
def form_valid(self, form):
obj = form.save(commit=False)
obj2 = Project.team_id
obj2 = obj.team_id
obj2.save()
print("sucess")
I would like that when a user creates a team, and if the team is created it successfully. Add automatically to the Project models with the corresponding team_id.
Any help would be appreciated. Thanks in advance!
Well, you can get the project with your user id which has a team_id null.
In the form_valid method of your TeamCreate() view:
def form_valid(self, form):
valid = super(TeamCreate, self).form_valid(form)
obj = form.save()
obj2 = Project.objects.get(project_hr_admin=self.request.user, team_id=None)
obj2.team_id = obj
obj2.save()
return valid
Here you just get the Project which has your user as admin and a null team_id.
Warning though, if an admin create two projects with no team, the request Project.objects.get(...) will raise an error as it will return more than one object.
So you must be sure it will always be one project and then one team created.
Hope it helps.

Categories

Resources