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.
Related
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
hi i am working on a django app. functionality that i am implementing is to let my user buy a internet pack from the website.
i have implemented the model, view, template and url so far. but in the form i am getting a drop down list of all the users registered on the app. i automatically want django to link the user with current logged in user and let him select the pack he wants to buy and populate the model(table) automatically.
My models.py
def get_deadline():
return dt.today() + timedelta(days=30)
class CustomUser(AbstractUser):
Address = models.CharField(max_length=500)
def __str__(self):
return self.username
class Plans(models.Model):
plan_name = models.CharField(max_length=50)
speed = models.IntegerField()
price = models.FloatField()
def __str__(self):
return self.plan_name
class Orders(models.Model):
user = models.ForeignKey(CustomUser, on_delete = models.CASCADE)
pack = models.ForeignKey(Plans, on_delete = models.CASCADE)
start_date = models.DateField(auto_now_add=True)
end_date = models.DateField(default=get_deadline())
is_active = models.BooleanField(default=True)
def __str__(self):
name = str(self.user.username)
return name
my views.py
class UserBuyPlan(LoginRequiredMixin, View):
template = 'plans/plan.html'
#success_url = reverse_lazy('autos:all')
success_url = reverse_lazy('home-home')
def get(self, request):
form = BuyPlanForm()
ctx = {'form': form}
return render(request, self.template, ctx)
def post(self, request):
form = BuyPlanForm(request.CustomUser,request.POST)
if not form.is_valid():
ctx = {'form': form}
return render(request, self.template, ctx)
make = form.save()
return redirect(self.success_url)
my forms.py (i tried searching online and found this init implementation but it doesnt work)
class BuyPlanForm(forms.ModelForm):
class Meta():
model = Orders
fields = "__all__"
def __init__(self, *args, **kwargs):
self.user = CustomUser
super(BuyPlanForm, self).__init__(*args, *kwargs)
self.fields['user'].initial = self.user
the photo of resulting form is attached below
ok so i found the answer.
just had to change my view function a bit.
if you want a detailed tutorial then please visit https://www.youtube.com/watch?v=-s7e_Fy6NRU&t=1840s
he explains in a much better way.
class UserBuyPlan(LoginRequiredMixin, CreateView):
model = Orders
template_name = 'plans/plan.html'
fields = ['pack']
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
and also had to add absolute url method to my orders model
class Orders(models.Model):
user = models.ForeignKey(CustomUser, on_delete = models.CASCADE)
pack = models.ForeignKey(Plans, on_delete = models.CASCADE)
start_date = models.DateField(auto_now_add=True)
end_date = models.DateField(default=get_deadline())
is_active = models.BooleanField(default=True)
def __str__(self):
name = str(self.user.username)
return name
def get_absolute_url(self):
return reverse('home-home')
rest everything is same.
I've been trying to set up a basic private messaging system in Django using the generic CreateView.
I am currently having trouble with the "Receiver"/"To" field in my form. I tried to make it so it was a drop down field with the options being followers of the logged-in user.
Currently, the field is populating with the correct usernames (in this case, "testuser1") but it is throwing an error saying this field needs to be populated with an instance of the User object.
ValueError: Cannot assign "'testuser1'": "Message.reciever" must be a "User" instance.
Is there a way to have the form pass in the object of the username that is selected?
Model:
class Message(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sender")
reciever = models.ForeignKey(User, on_delete=models.CASCADE, related_name="reciever")
subject = models.CharField(max_length=128, default="-")
content = models.TextField()
send_date = models.DateTimeField(default=timezone.now)
User Relationships Model:
class UserRelationships(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="following")
following_user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="followers")
created = models.DateTimeField(auto_now_add=True)
UPDATED Form:
class MessageCreateForm(forms.ModelForm):
class Meta:
model = Message
fields = ['sender', 'reciever', 'subject', 'content']
widgets = {'sender': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
follower_objects = kwargs.pop('follower_objects')
super(MessageCreateForm, self).__init__(*args, **kwargs)
self.fields['reciever'] = RecieverModelChoiceField(queryset=User.objects.filter(username__in=follower_objects))
View:
class MessageCreateView(LoginRequiredMixin, CreateView):
model = Message
template_name = 'message/compose.html'
form_class = MessageCreateForm
def get_initial(self):
initial = super().get_initial()
initial['sender'] = self.request.user
return initial
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
user = self.request.user
followers = user.followers.values_list('user_id', flat=True)
follower_objects = []
kwargs['user'] = self.request.user
kwargs['follower_objects'] = follower_objects
for id in followers:
follower = User.objects.get(id=id)
follower_objects.append(follower)
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
You have to use forms.ModelChoiceField instead of forms.ChoiceField
ForeignKey (model) > ModelChoiceField (form) - Default widget: Select
ModelChoiceField has attribute queryset.
You can filter field reciever.queryset directly in MessageCreateForm.__init__ method.
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(MessageCreateForm, self).__init__(*args, **kwargs)
self.fields['reciever'].queryset = user.followers
UPDATE:
You can set a custom ModelChoiceField that will return any label you want (more info).
from django.forms import ModelChoiceField
class RecieverModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.username
or
def __init__(self, *args, **kwargs):
....
self.fields['reciever'].label_from_instance = lambda obj: "%s" % obj.username
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())
I have a simple Group model that users can be added.
class Group(models.Model):
name = models.CharField(max_length=50)
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='admin_on_group')
date_created = models.DateTimeField(auto_now_add=True)
date_modifies = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
I have a basic CreateView for the group. The logged in user who creates the group get saved in the field created_by. I however ALSO want to save the same logged in user in the field users so that he can participate as a normal member of the group. The problem is that the view ends up ONLY saving the logged in user and the other users passed in from the form field users are not saved.
For example, If a user called 'george' creates a group, he should be added in the created_by and users as well. As of now, when I select other users in the form, only george gets saved in both fields.
class GroupCreateView(CreateView):
form_class = GroupForm
template_name = "groups/group_create.html"
def form_valid(self, form):
form = form.save(commit=False)
form.created_by = self.request.user
form.save()
# Apparently you can only add M2M relationships saves after first
# saving
form.users.add(User.objects.get(pk = self.request.user.pk))
return HttpResponseRedirect(reverse('group_list'))
def get_form_kwargs(self):
kwargs = super(GroupCreateView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
I have a modelForm that has the following outline.
Note: The initial data passed in the self.fields['users'] below also doesn't show. I have also used a custom model that has phone_number as the USERNAME_FIELD. The querysets passed in the self.fields['users'] works.
class UserModelChoiceField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return obj.get_full_name()
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = ('name', 'users', )
def __init__(self, *args, **kwargs):
# popping the user from kwargs dictionary that has been
# passed in CreateView
user = kwargs.pop('user', None)
self.user = user # setting self.user to be equal to user above
super(GroupForm, self).__init__(*args, **kwargs)
self.fields['users'] = UserModelChoiceField(
queryset=User.objects.exclude(phone_number=str(user)),
initial=User.objects.get(phone_number=str(user))
)
Since you've saved the form with commit=False, you need to call the form's save_m2m() method to save the many-to-many data after you have saved the instance.
def form_valid(self, form):
instance = form.save(commit=False)
instance.created_by = self.request.user
instance.save()
form.save_m2m()
# Apparently you can only add M2M relationships saves after first
# saving
instance.users.add(self.request.user)
return HttpResponseRedirect(reverse('group_list'))
Note that I've changed the line to instance = form.save(commit=False) to make it clearer that save() returns an instance, and so that you still have access to the form.