How to get ChoiceField data based on current user - python

I have a model StaffProfile.while creating a form for Visiti want to get staff_user data(Based on current user) to that ChoiceField (to_meet).
models.py
class StaffProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
related_name="user_profile")
staff_user = models.ManyToManyField(User, null=True, blank=True,
related_name="staff_user")
class Visit(models.Model):
name = models.CharField(max_length=200, name="name")
gender = models.CharField(choices=GENDER_CHOICE, max_length=1, name="gender")
mobile = models.CharField(max_length=18, default="", name="mobile")
to_meet = models.ForeignKey(User, on_delete=models.CASCADE)
forms.py
class VisitForm(forms.ModelForm):
to_meet = forms.ChoiceField(choices=[], required=False, label="Select Staff")
class Meta:
model = Visit
fields = ("__all__")
def __init__(self, *args, **kwargs):
super(VisitForm, self).__init__(*args, **kwargs)
self.fields['to_meet'].choices = StaffProfile.objects.filter(user=request.user).values_list("staff_user")

Initially override the __init__() method of your view
class VisitForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(VisitForm, self).__init__(*args, **kwargs)
self.fields['to_meet'].choices = [self.request.user]
to_meet = forms.ChoiceField(choices=[], required=False, label="Select Staff")
class Meta:
model = Visit
fields = "__all__" # small typo here
Then, in your view,
def foo_view(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = VisitForm(request.POST,request=request)
# check whether it's valid:
if form.is_valid():
# do somrthing
.....

You need to pass the request from view to form. For example:
def some_view(request):
form = VisitForm(request=request)
# rest of the code
and use it in the form:
def __init__(self, *args, **kwargs):
request = kwargs.pop('request')
super(VisitForm, self).__init__(*args, **kwargs)
self.fields['to_meet'].choices = StaffProfile.objects.filter(user=request.user).values_list("staff_user")

Related

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

How to display a current user only in django user form

class Book(models.Model):
description = models.CharField(max_length=10)
pdf = models.FileField(upload_to='books/pdfs/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.description
def delete(self, *args, **kwargs):
self.pdf.delete()
super().delete(*args, **kwargs)
#forms.py
class BookForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(BookForm, self).__init__(*args, **kwargs)
class Meta:
model = Book
fields = ('description', 'pdf', 'user')
When i run an application it shows all the users, I want to restrict to only current user who is logged in.

Automatically create a record in another model after using CreateView

I have two models (OK 3 models since AssignedAsset is a subclass of Asset), one that tracks assets and another that tracks the history of owners for that asset. When I create a new asset using CreatView I would like to automatically have it create a History record as well.
models.py
class Asset(models.Model):
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
serial_number = models.CharField(max_length=100)
po = models.ForeignKey('purchaseorders.PurchaseOrder', default=None, blank=True, null=True)
location = models.ForeignKey('locations.Plant')
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.slug
def save(self):
forslug = "{0.make}-{0.model}-{0.serial_number}".format(self)
self.slug = slugify(forslug)
super(Asset, self).save()
class AssignedAsset(Asset):
user = models.ForeignKey(User)
def __str__(self):
return self.slug
class AssignedHistory(models.Model):
assset = models.ForeignKey('Asset')
user = models.ForeignKey(User)
date = models.DateField()
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.slug
def save(self):
forslug = "{0.asset}-{0.date}".format(self)
self.slug = slugify(forslug)
super(AssignedHistory, self).save()
Here is my view.
class NewAssignedAsset(CreateView):
form_class = AssignedAssetForm
template_name = 'createassignedasset.html'
success_url = '/assets'
And my forms.py
class AssignedAssetForm(forms.ModelForm):
class Meta:
model = AssignedAsset
fields = ['make', 'model', 'serial_number', 'location', 'user', 'po']
def __init__(self, *args, **kwargs):
super(AssignedAssetForm, self).__init__(*args, **kwargs)
#Filter out PO's that have packingslips (otherwise you will quickly have a ridicously big drop-down of every PO in the system)
self.fields['po'] = forms.ModelChoiceField(required=False, queryset=PurchaseOrder.objects.filter(packing_slip=''))
I thought maybe I could have it create the history when it gets the success URL, so I tried this in my view:
import time
def today():
return time.strftime ("%m/%d/%Y")
class NewAssignedAsset(CreateView):
form_class = AssignedAssetForm
template_name = 'createassignedasset.html'
def get_success_url(self):
history = AssignedHistory.objects.create(assset=self.object, user=self.object.user, date=today())
return '/assets'
But this throws a TypeError:
save() got an unexpected keyword argument 'force_insert'
Anything that would point me in the right direction would be appreciated.
You can do it at multiple levels(DB level, form level).
In your case, I'll say you just need to override the save() of your AssignedAssetForm. (Assuming you set user in context of form)
def save(self, *args, **kwargs):
assigned_asset = super(AssignedAssetForm, self).save(*args, **kwargs)
user = self.context.get(u'user')
if user:
assigned_asset_history = AssignedHistory(asset=assigned_asset, user=user, date=datetime.date.today())
assigned_asset_history.save()
return assigned_asset
** I am not sure about the context part, you may have to look into how to use user in form.
You should write your Asset.save() and AssignedHistory.save() as:
def save(self, **kwargs):
...
super(YourModel, self).save(**kwargs)
...
Note the **kwargs. They allow you to accept optional parameters (and a Model.save() has a few).

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

Django Userprofile form extension

Im extending the django User model with User profile like this :
class UserProfile(BaseModel):
user = models.ForeignKey(User, unique=True, related_name="profile")
city = models.CharField(_("City"), max_length=200)
tel = models.CharField(_("Phone Number"), max_length=50,
help_text=_("(+ Country Code) (Area Code) (Your phone number)"))
description = models.TextField(null=True, blank=True,
help_text = _("Small description about yourself."))
photo = models.ImageField(max_length=255, upload_to="profiles/")
def __unicode__(self):
return "%s %s" % (self.user.first_name, self.user.last_name)
def profile_image(self):
return settings.DEFAULT_PROFILE_IMAGE
and this is the form Im using on the front end in order to edit the profile from there:
class UserProfileForm(forms.ModelForm):
# uniForm Helper
helper = FormHelper()
helper.form_id = "edit_profile_form"
helper.form_class = 'mlForm'
layout = Layout(
Fieldset('',
'country', 'city',
'tel', 'photo', 'description',
)
)
helper.add_layout(layout)
# Submit button(s)
submit = Submit('submit','Submit')
helper.add_input(submit)
cancel = Submit('cancel','Cancel')
helper.add_input(cancel)
class Meta:
model = UserProfile
fields = ['city',
'tel', 'photo', 'description',]
def __init__(self, request, *args, **kw):
super(UserProfileForm, self).__init__(*args, **kw)
self.request = request
def save(self, *args, **kw):
edit_profile_form = super(UserProfileForm, self).save(*args, **kw)
return edit_profile_form, "User successfully modified."
What I want to do is add the User email field in the form so that the user can also edit this field from the frontend.
How would I do that?
Hope, this will help
class UserProfileForm(forms.ModelForm):
email = forms.EmailField(label=u'Email address',required=True)
class Meta:
model = UserProfile
exclude = ['user',]
def __init__(self, *args, **kw):
super(UserProfileForm, self).__init__(*args, **kw)
self.fields['email'].initial = self.instance.user.email
self.fields.keyOrder = ['your','fields','you','want','to','display','including','email']
def save(self, *args, **kw):
super(UserProfileForm, self).save(*args, **kw)
self.instance.user.email = self.cleaned_data.get('email')
self.instance.user.save()
this recipe works for me (django 1.3).
in views.py must be something like this:
def profile(request,id):
if request.method == 'POST':
user = get_object_or_404(User,pk=id)
profile = user.profile
form = UserProfileForm(data = request.POST,instance = profile)
if form.is_valid():
form.save()
Send the two objects-forms (UserEditForm and UserProfileForm) into the template form and submit them together.

Categories