Accessing ModelForm queryset Object field in template - python

In my ModelForm, I am filtering the project_users to a certain set of all Users. How can I customize the checkboxes to show a users first_name and last_name?
Currently showing their email address as the checkbox label.
models.py
class Project(models.Model):
project_business_profile = models.ForeignKey(BusinessProfile, on_delete=models.CASCADE)
project_users = models.ManyToManyField(User, related_name='project_users')
...
def __str__(self):
return str(self.project_name)
views.py
class EditProject(LoginRequiredMixin, View):
login_url = '/signin'
redirect_field_name = 'signin'
def get(self, request, project_id):
...
form = EditProjectForm(instance=project)
...
forms.py
class EditProjectForm(ModelForm):
project_users = forms.ModelMultipleChoiceField(
widget = forms.CheckboxSelectMultiple,
queryset = User.objects.none()
)
class Meta:
model = Project
fields = ['project_users']
def __init__(self, *args, **kwargs):
super(EditProjectForm, self).__init__(*args, **kwargs)
current_project = self.instance
current_business = current_project.project_business_profile
users = current_business.business_users.all()
self.fields['project_users'].queryset = current_business.business_users.all()
// Spits out the correct users however I need to access other user fields of User in template. Name etc
template
{{form.as_p}}

I'm not entirely sure I understand, do you just want to change the label which shows your user? If so something like this may work:
forms.py
class EditProjectForm(ModelForm):
project_users = forms.ModelMultipleChoiceField(
widget = forms.CheckboxSelectMultiple,
queryset = User.objects.none()
)
class Meta:
model = Project
fields = ['project_users']
def __init__(self, *args, **kwargs):
super(EditProjectForm, self).__init__(*args, **kwargs)
current_project = self.instance
current_business = current_project.project_business_profile
users = current_business.business_users.all()
self.fields['project_users'] = UserChoiceField(queryset=users)
// Spits out the correct users however I need to access other user fields of User in template. Name etc
class UserChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
user = User.objects.get(id=obj.id)
return user.get_full_name()

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

Private messaging system Django

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

Django formset - how to give a different queryset for each form within the modelformset

I have a form for a Model - ClinicallyReportedSample, which links to a Sample model.
I am trying to create a formset for ClinicallyReportedSample where, based on a queryset of Sample, a specific amount of forms are displayed so that the user can add data.
Currently, the Sample model has entries, but the ClinicallyReportedSample model is completely empty:
models:
class Sample(models.Model):
request_number = models.PositiveIntegerField()
year = models.PositiveIntegerField()
class Meta:
db_table = "sample"
unique_together = (('request_number', 'year'),)
def __str__(self):
return("%s/%s" %(self.request_number, self.year))
class ClinicallyReportedSample(models.Model):
sample_id = models.ForeignKey(Sample,
on_delete=models.CASCADE,
db_column='sample_id')
reported = models.BooleanField(default=False)
evidence = models.TextField(null=True, blank=True)
... other fields ...
class Meta:
db_table = "clinically_reported_sample"
unique_together = (('sample_id'),)
def __str__(self):
clinically_reported_sample = str(self.sample_id)
return(clinically_reported_sample)
I want ClinicallyReportedSample model forms, within a formset, that relate to a queryset of Sample model.
For example, Sample objects with pk 1, 2 and 3:
forms.py:
class BaseCRSFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# self.queryset = ClinicallyReportedVariant.objects.none()
class CRSForm(forms.ModelForm):
class Meta:
model = ClinicallyReportedSample
fields = ('sample_id', 'evidence',)
def __init__(self, *args, **kwargs):
super(CRSForm, self).__init__(*args, **kwargs)
So I try to do this using queryset in my formset
views.py:
def get(self, request, *args, **kwargs):
sample_obj = Sample.objects.filter(id__in=[1, 2, 3])
formset = modelformset_factory(
ClinicallyReportedSample,
form=self.crsform,
formset=BaseCRSFormSet,
extra=3,
)
formset = formset(queryset=sample_obj)
but this displays as three forms, with ALL Sample objects, the queryset does not work. Is this the correct way to go about this?
You'll need to set the default Sample queryset to none:
class CRSForm(forms.ModelForm):
class Meta:
model = ClinicallyReportedSample
fields = ('sample_id', 'evidence',)
sample_id = forms.ModelChoiceField(queryset=Sample.objects.none())
def __init__(self, *args, **kwargs):
super(CRSForm, self).__init__(*args, **kwargs)
And then when you make a formset instance manually assign the queryset like so:
def get(self, request, *args, **kwargs):
sample_obj = Sample.objects.filter(id__in=[1, 2, 3])
formset = modelformset_factory(
ClinicallyReportedSample,
form=self.crsform,
formset=BaseCRSFormSet,
extra=3,
)
formset = formset(queryset=sample_obj)
for form in formset:
form.fields['sample_id'].queryset = sample_obj
Note that you'll also have to manually set the queryset in your POST function too otherwise it won't validate.

(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.

forms.ModelForm Restrict to User Who Created the Model

Simply asked: In a form, how do you restrict the display of a many-to-many relationship to the records the user has defined himself?
In an effort to keep things as DRY as possible, I'm using forms.ModelForm.
Here in Example, a user can create feed-items which run on a schedule. Technically the feeds and the items are connected to the user by user = models.ForeignKey(User) however that in itself is not enough to restrict the association of the two through the user himself.
While I'd love to inherently restrict association of the two THROUGH the user, I'd be happy to simply restrict the display of the items via user and simply validate afterwards. Example data below:
class Example(models.Model):
global CHOICES_DAYS
global CHOICES_HOURS
user = models.ForeignKey(User)
label = models.CharField(max_length=180)
title = models.CharField(max_length=1000, blank=True)
content = models.TextField(max_length=10000, blank=True)
Example_feeds = models.ManyToManyField(ExampleFeed, blank=True)
hourly_schedule = models.CharField(choices=CHOICES_HOURS, max_length=2, default=4)
weekly_schedule = models.CharField(choices=CHOICES_DAYS, max_length=5, default=1)
last_used = models.DateTimeField(default=timezone.now, blank=False)
def __str__(self):
return self.label
class Meta:
ordering = ('last_used',)
And here is the form, of which I'm using forms.ModelForm to keep things as DRY as possible:
class ExampleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_id = ''
self.helper.form_class = ''
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
super(ExampleForm, self).__init__(*args, **kwargs)
class Meta():
model = Example
fields = [
'label',
'title',
'content',
'example_feeds',
'hourly_schedule',
'weekly_schedule']
You need to pass the request.user to the form when you are instantiating it in your view and then make use of that request object to restrict the form field's queryset.
class ExampleForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
self.fields['mym2mfield'].queryset = Example.objects.filter(example_feeds__user=request.user)
and in your view (assuming it's a class based view):
class MyView(FormView):
def get_form(self, form_class):
return form_class(self.request, self.get_form_kwargs())

Categories