Attaching the logged-in User to object after object creation - python

I am trying to have a logged in User fill out a form to create a Group. On Group creation, I need the User to automatically be added to the Group.
For this problem, we are working with two models - User and Group.
User is the default model provided by Django.
Group is defined like so:
class Group(models.Model):
name = models.CharField(max_length=255, unique=True)
admins = models.ManyToManyField(User, default=1, related_name='user_username')
all_users = models.ManyToManyField(User, default=1)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('home')
def get_admins(self):
return ", ".join([u.username for u in self.admins.all()])
def add_admin(self, user):
self.admins.add(user)
def get_all_users(self):
return ", ".join([u.username for u in self.all_users.all()])
def add_user(self, user):
self.all_users.add(user)
self.save()
def is_admin(self, user):
if user in self.admins.all():
return True
else:
return False
And the view I'm trying to refactor is:
#login_required
def user_generated_group(request):
if request.method == 'POST':
form = GroupForm(request.POST)
user = request.user
if form.is_valid():
group = Group.objects.create(name=form.cleaned_data['name'])
group.add_admin(user)
group.add_user(user)
group.save()
return HttpResponseRedirect(reverse('home'))
else:
form = GroupForm()
context = {
'form': form,
'type': 'group',
'sidebar': Sidebar(request),
}
return render(request, 'form.html', context)
Here is the GroupForm:
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = '__all__'
exclude = ['all_users', 'admins', ]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'class': 'input'})
The goal is to utilize Django's built-in CreateView. The refactored view so far looks like:
class CreateGroup(LoginRequiredMixin, CreateView):
model = Group
form_class = GroupForm
template_name = 'form.html'
I have yet to implement the add_user and add_admin logic to this view. The hope is that I can manage these methods elsewhere.
I'm unsure of where to go from here. Should this logic be handled by the User or the Group?
If by the Group, should I be using a Manager?
If by the User, should I create a custom User model?

You can override the form_valid [Django-doc] method, to add the self.request.user to the group, like:
from django.http import HttpResponseRedirect
class CreateGroup(LoginRequiredMixin, CreateView):
model = Group
form_class = GroupForm
template_name = 'form.html'
def form_valid(self, form):
self.object = group = form.save()
group.all_users.add(self.request.user)
return HttpResponseRedirect(self.get_success_url())

Related

Filter dropdown in django forms

In forms, I am trying to filter marketplace drop down field that belong to the logged in user based on its group. Its listing all the dropdown field items. I tried below but I think something is wrong with the filter part.
class InfringementForm(ModelForm):
def __init__(self, user, *args, **kwargs):
super(InfringementForm,self).__init__(*args, **kwargs)
self.fields['marketplace'].queryset =
Marketplace.objects.filter(groups__user=self.user)
class Meta:
model = Infringement
class Meta:
ordering = ['-updated', '-created']
def __str__(self):
return self.name
fields = ['name', 'link', 'infringer', 'player', 'remove', 'status',
'screenshot','marketplace']
models.py
class Marketplace (models.Model):
name = models.CharField(max_length=100)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
groups = models.ForeignKey(Group, on_delete=models.CASCADE,default=1)
Try this inside __init__() method:
def __init__(self, user, *args, **kwargs):
self.user = user
super(InfringementForm,self).__init__(*args, **kwargs)
self.fields['marketplace'].queryset = Marketplace.objects.filter(groups__user=self.user)
final answer is adding self.user = user in the forms and adding user in the view.
forms.py
class InfringementForm(ModelForm):
def __init__(self, user, *args, **kwargs):
self.user = user
super(InfringementForm,self).__init__(*args, **kwargs)
self.fields['marketplace'].queryset =
Marketplace.objects.filter(groups__user=self.user)
class Meta:
model = Infringement
fields = ['name', 'link', 'infringer', 'player', 'remove', 'status',
'screenshot', 'marketplace']
views.py
#login_required(login_url='login')
def createInfringement(request):
user=request.user
form = InfringementForm(user=request.user)
if request.method == 'POST':
form = InfringementForm(user, request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home') context ={'form': form} return render (request, 'base/infringement_form.html', context)
Try this solution...
Basically groups is foreign key in Marketplace model it returns a full object of the Group model.
you tried to filter with the full object it's not possible in a query, so you can filter using id of the user instance
Code becomes like this
class InfringementForm(ModelForm):
def __init__(self, user, *args, **kwargs):
super(InfringementForm,self).__init__(*args, **kwargs)
self.fields['marketplace'].queryset =
Marketplace.objects.filter(groups__user__id=self.user.id)
class Meta:
model = Infringement

Django Model Form is not saving data to database, but object is created

I'm using a model form and a class based view and I'm trying to save user post model to db. The problem is that UserPost object is created (I've printed it in console and it has all the data and every time when I create new it has new id, so it seems it works) but nothing is saved to database. I'm not sure where the problem might be. Do you have any idea?
views.py
class CompletePost(View):
def get(self, request, *args, **kwargs):
post_form=myForms.UploadPostForm()
return render(request,'shop/create_post.html',{'post_form':post_form})
def post(self, request, *args, **kwargs):
post_form=myForms.UploadPostForm(request.POST)
print(request.user.id)
if post_form.is_valid():
user_post_v=post_form.save()
transaction.commit()
models.py
class UserPost(models.Model):
user_id=models.ForeignKey(Account,on_delete=models.CASCADE)
title=models.CharField(max_length=255)
text=models.TextField(null=True)
category=models.ForeignKey(Category,null=True,on_delete=models.SET_NULL)
is_used=models.BooleanField(default=False)
price=models.IntegerField(default=0)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
is_active = models.BooleanField(default=True)
def __unicode__(self):
return self.id
def get_absolute_url(self,**kwargs):
return reverse_lazy('index')
User FormView for views with forms:
from django.views.generic import FormView
class CompletePost(FormView):
template_name = 'your_template.html'
form_class = UploadPostForm
def form_valid():
new_post = form.save(commit=False)
category = Category.objects.get(pk=self.kwargs['pk']) # or just query your category the way you would do it
user = self.request.user
new_post.category = category
new_post.user_id = user
new_post.save()
return redirect('index') # or wherever you wanna redirect after submitting

How to use different field as label for modelformset?

I'm creating an inventory management system using django as a framework. I have a simple model with a product column and an order_amount column. The only part I want users being able to update using the modelformset is the order_amount column. Django automatically labels each form with the field name "Order Amount". What I would like it to do is to label each form as the instance it is updating from the Product field.
models.py
class Sysco_Products(models.Model):
Products = models.CharField(max_length = 200)
order_amount = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.Products
class meta:
managed = True
db_table = 'sysco_products'
forms.py
from django import forms
from .models import Sysco_Products
class orderform(forms.ModelForm):
class Meta:
model = Sysco_Products
fields = ('order_amount',)
views.py
class SyscoOrder(TemplateView):
template_name= "SyscoOrder.html"
def get(self, request):
OrderFormSet = modelformset_factory(Sysco_Products, fields=('order_amount',))
context = {
'OrderFormSet' : OrderFormSet,
}
return render(request, self.template_name, context)
def post(self, request):
OrderFormSet = modelformset_factory(Sysco_Products, fields=('order_amount',))
formset = OrderFormSet(request.POST,)
if formset.is_valid():
formset.save()
return redirect('Order')
context ={
'formset' : formset,
}
return render(request, self.template_name, context)
You mean something like field.label = f"{sysco_product_instance.Products} amount:"?
This should work:
from django.utils.translation import gettext
class orderform(forms.ModelForm):
class Meta:
model = Sysco_Products
fields = ('order_amount',)
def __init__(self, *args, **kwargs):
super(self, orderform).__init__(*args, **kwargs)
if self.instance.Products.strip():
self.fields['order_amount'].label = gettext("%(product_name)s amount:").format(product_name=self.instance.Products)

(Django) Limited ForeignKey choices by Current User in UpdateView

I recently was able to figure out how to do this in the CreateView, but the same is not working for the UpdateView (Here's the original post on how to do it in the CreateView: (Django) Limited ForeignKey choices by Current User)
Essentially, I need it to display only the Universes created by the currently logged in user, but by default, it displays all universes.
When I try to set a form_class and have it mimic the solution for CreatView, it spits out an improperly configured error.
models.py:
class Universe(models.Model):
user = models.ForeignKey(User,related_name='universe',on_delete=models.CASCADE)
name = models.CharField(max_length=100, unique=True)
description = models.TextField(max_length=2000,blank=True,default="")
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('universe:singleuniverse',kwargs={'pk': self.pk})
class Meta:
ordering = ['name']
unique_together = ['user','name']
class Character(models.Model):
user = models.ForeignKey(User,related_name='characters',on_delete=models.CASCADE)
universe = models.ForeignKey("story_universe.Universe", on_delete=models.CASCADE)
name = models.CharField(max_length=255,unique=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('character_developer:singlecharacter',kwargs={'pk': self.pk})
class Meta():
ordering = ['name']
unique_together=['user','name']
views.py:
class UpdateCharacter(LoginRequiredMixin,generic.UpdateView):
model = Character
fields = ('universe','name')
template_name = 'character_developer/character_update_form.html'
UPDATE
The error was:
Error: ImproperlyConfigured at /characters/update/3/
UpdateCharacter is missing a QuerySet. Define UpdateCharacter.model, UpdateCharacter.queryset, or override UpdateCharacter.get_queryset().
and here's what the code looked like to get the error:
views.py:
class UpdateCharacter(LoginRequiredMixin,generic.UpdateView):
template_name = 'character_developer/character_update_form.html'
form_class = UpdateForm
def get_form_kwargs(self):
kwargs = super(UpdateCharacter,self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
forms.py
class UpdateForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
user = kwargs.pop('user')
super(UpdateForm,self).__init__(*args,**kwargs)
self.fields['universe'].queryset = Universe.objects.filter(user=user)
class Meta:
model = Character
fields = ('universe','name')
I believe you need the following in your views.py (almost an exact extension of your CreateCharacter):
class UpdateCharacter(LoginRequiredMixin, generic.UpdateView):
model = Character
template_name ='character_developer/character_create.html'
form_class = UpdateForm
def get_form_kwargs(self):
kwargs = super(UpdateCharacter,self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self,form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save
return super().form_valid(form)
I would caveat the above - make sure that the user instance in the request object is that of the currently logged in user, and has sufficient permission to update their own user - and can't somehow make a request on their behalf.

Referencing the current user in Class Based Views (CBV)

I've implemented a form where I require fields in the User object to be populated (firstname, lastname, email) as well as fill out a new membership object. I've implemented this with a Function Based View (FBV) but I feel like I should be able to do this with a Class Based View (CBV). The heart of the problem seems to be referencing the current user in a form without passing in the user object. In FBV it's easy to do but I can't find any examples using CBV. I'm thinking that I must be missing something here.
Here is my code
models.py
class Membership(models.Model):
"""Represents an active membership of a user. Both the start_date and
end_date parameters are inclusive."""
DEFAULT_DURATION = 365 # the default number of days a membership is active
start_date = models.DateField(auto_created=True)
end_date = models.DateField(null=True)
membership_type = models.ForeignKey(MembershipType)
referral = models.ForeignKey(User, related_name='membership_referral', null=True)
# Contact Info
phone = PhoneNumberField()
# Address Fields
address_1 = models.CharField(max_length=255)
address_2 = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=64)
state = USStateField()
zip_code = USPostalCodeField()
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
"""Overload the save function to set the start and end date."""
self.start_date = datetime.date.today()
self.end_date = (self.start_date +
datetime.timedelta(days=self.membership_type.period))
super().save()
#property
def is_active(self):
return self.end_date >= datetime.date.today()
forms.py
class MembershipForm(ModelForm):
"""The Form shown to users when enrolling or renewing for membership."""
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
_fields = ('first_name', 'last_name', 'email',)
_initial = model_to_dict(self.user, _fields) if self.user is not None else {}
super(MembershipForm, self).__init__(initial=_initial, *args, **kwargs)
self.fields.update(fields_for_model(User, _fields))
self.fields['referral'].required = False
class Meta:
model = Membership
fields = ['membership_type', 'referral', 'phone', 'address_1',
'address_2', 'city', 'state']
zip_code = USZipCodeField(max_length=5, required=True)
def save(self, *args, **kwargs):
self.user.first_name = self.cleaned_data['first_name']
self.user.last_name = self.cleaned_data['last_name']
self.user.email = self.cleaned_data['email']
self.user.save()
profile = super(MembershipForm, self).save(*args, **kwargs)
return profile
views.py
#login_required
def enroll(request):
template_name = 'enroll.html'
if request.method == 'POST':
form = MembershipForm(request.POST, user=request.user)
if form.is_valid():
form.save()
return redirect('/')
else:
form = MembershipForm(user=request.user)
return render(request, template_name, {'form': form})
You can access current user in class based view by self.request.user. It can be set in FormView by redefining validate method like this:
class YourView(CreateView)
...
def form_valid(self, form):
form.instance.user = self.request.user
return super(YourView, self).form_valid(form)
I have used CreateView instead of FormView in example because for edit you should check current instance's user in additional for security purposes.
Although your question mentions CBV, yet in the code you are using FBV.
In FBV you have access to request variable being passed. You can use request.user in this case.
In case of CBVs, Django allows you to access request object as self.request. In the implementation of default 'django.views.generic.base.View' class of CBV, they do this as first thing.
Check 4th line of def view as part of as_view in this code - https://github.com/django/django/blob/master/django/views/generic/base.py
All the objects, including user as part of request can be accessed as self.request.user.

Categories