django - pass request.user to ModelForm - django-forms

i am having a tricky issue. In my views.py i am passing a form in a DetailView. But i am not using a FormMixin. That has the reason that i only want my template to render the form. In the template i use that form to trigger an UpdateView.
class UpdateDetailView(LoginRequiredMixin, DetailView):
model = Balance
def get_context_data(self, **kwargs):
context = super(UpdateDetailView, self).get_context_data(**kwargs)
context['form'] = ManagerForm
return context
def get_queryset(self, *args, **kwargs):
request = self.request
pk = self.kwargs.get('pk')
select = self.kwargs.get('select')
queryset = Balance.objects.filter(pk=pk).filter(owner = request.user).filter(select = select)
return queryset
class BalanceUpdateView(LoginRequiredMixin, UpdateView):
form_class = ManagerForm
model = Balance
def get_success_url(self):
return reverse('url-year', kwargs={'year': self.object.year, 'select': self.object.select})
So far, so good. The issue is that the form in the UpdateDetailView the ChoiceFields are showing select option of other users which one is not supposed to see. Thus i need to override the queryset of the Modelform. Usually i could use the get_form_kwargs method to pass the request.user to the form. But since the UpdateDetailView is not a FormView it doesnt have that (i know, coz i tried desperately). I also tried context['form'] = ManagerForm(initial = {'user': self.request.user}) in the get_context_data method. But i was unable to access user properly with the __init__ method in the forms.py. I got a key error. If i passed it as arg then i get an attribute error. Does anyone have a solution to that problem? Do i need to use a FormMixin?
Thank you for your replies!

Related

I'm trying to add a dynamic queryset to a model-form/formset field from a url kwarg <int:url_kwarg>, no luck so far

Edit: It appears that the problem I'm experiencing is directly related to the formset_factory() call. For some reason or other, I have been unable to access the kwargs from the view if I pass the form through the function.
I'm building a web app that utilizes high normalization in the data-structure. There are many many-to-many and one-to-many relationships to prevent excess null records and database-bloat. I want to add more entries to a model while excluding existing entries from the model.choice field.
my code looks like this:
the form:
class ExtraAddForm(forms.ModelForm):
def __init__(self, url_kwarg, *args, **kwargs):
super(ExtraAddForm, self).__init__(self, *args, **kwargs)
list_to_exclude = []
query_target = models.Model.objects.get(fk_id=url_kwarg)
for object in query_target:
list_to_exclude.append(object.fk_id.id)
new_queryset = models.Model.objects.exclude(fk_id__in=list_to_exclude)
self.fields['fk_id'].queryset= new_queryset
class Meta:
model = models.Model
fields= ['fk_id','field_b'}
the view:
class AddOjbectsView(FormView):
formset = formset_factory(ExtraAddForm(url_kwarg), can_delete=True)
model = models.Model
url_kwarg = 'url_kwarg'
form_class = formset
template_name = 'some-template.html'
extra_context = {'some_object': models.Model2.objects.all,
'model_object': models.Model.objects.all,
'formset': formset,
'view_type_create': True
}
def __init__(self, *args, **kwargs):
kwargs['url_kwarg']= self.kwargs.get(self.url_kwarg)
super().__init__(self,*args,**kwargs)
def get(self, request, *args, **kwargs):
request.session['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
#this works so I'm not re-typing it
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'url_kwarg' not in kwargs:
kwargs['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get_context_data(**kwargs)
#this works, but only in the get_context. Its not working as a solution to my problem.
def get_success_url(self):
#this works, not re-typing
My template has Javascript to handle multiple formsets, and I've tested it with a non-dynamic queryset. The only piece I'm having trouble with is taking the keyword argument from the URL and passing it to the form at init.
Have you tried using FormView.get_form_kwargs method?
Its described in docs
class AddOjbectsView(FormView):
def get_form_kwargs(self):
form_kwargs = super().get_form_kwargs()
form_kwargs['url_kwarg'] = self.kwargs['url_kwarg']
return form_kwargs
It would seem that the answer to my problem is to deprecate the process in favor of session variables at form validation, making use of the clean data functions. My methods above were generated from ignorance of the order of django operations, and shouldn't preoccupy anyone any further.

Serializer `validate()` not getting called on `is_valid()` on POST

I want to create a non class-based form to facilitate uniform logging in by users across all front-end apps. Currently, it looks like this
My serializer class:
class EmployeeLoginSerializer(serializers.Serializer):
username = serializers.CharField(min_length=6)
password = serializers.CharField(min_length=6)
def validate_credentials(self, attrs):
print('validating')
try:
emp: Employee = Employee.objects.get(username=attrs['username'])
if crypto.verify_password(attrs['password'], emp.password_hash):
return attrs
except Exception:
raise serializers.ValidationError("Incorrect username or password")
raise serializers.ValidationError('Incorrect username or password')
My view class:
class TestView(APIView):
serializer_class = EmployeeLoginSerializer
def get(self, request, *args, **kwargs):
return Response({'Message': 'Get works'})
def post(self, request, *args, **kwargs):
print(request.POST)
serializer = self.serializer_class(data=request.POST)
if serializer.is_valid():
return Response({'Message': 'Credentials were correct'})
My issue is that serializer.is_valid() doesn't seem to be calling on validate automatically. I know I could just call serializer.validate() manually but all the docs and questions on StackOverflow seem to show validate() being called by is_valid() automatically so I get that feeling that that wouldn't be the best practice. Is there something I'm missing?
The is_valid() method will call validate() method of the serializer and validate_FIELD_NAME() methods.
In your code, the validate_credentials() seems a regular class method which won't detect by DRF since the credentials isn't a field on the serializer.

clean django api in serilizer

i write a django api i would like to know if reminder field changed then the Appointment model object save current user.
i used this link
See object changes in post_save in django rest framework
and write this code
class AppointmentBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
model = Appointment
read_serializer_class = AppointmentSerializer
write_serializer_class = AppointmentCreateSerializer
reminder_change = False
def perform_update(self, serializer):
if 'reminder' in serializer.validated_data:
self.reminder_change = True
serializer.save()
def update(self, request, *args, **kwargs):
super(AppointmentBackOfficeViewSet, self).update(request, *args, **kwargs)
instance = self.get_object()
instance.user = request.user
if self.reminder_change:
instance.reminder_user = request.user
instance.save()
res = self.write_serializer_class(instance).data
return Response(res)
class AppointmentCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Appointment
exclude = ['created_time', 'modified_time']
is there a way to write this code clearer than this :
dont use self.reminder_change class field is there better way?
may be move this lines to serializer??(in serializer dont access to request.user)
Removed unecessary fields and update method:
class AppointmentBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
model = Appointment
read_serializer_class = AppointmentSerializer
write_serializer_class = AppointmentCreateSerializer
def perform_update(self, serializer):
# Here you can set attributes directly to the serializer update method
# It's like setting attribute directly to the updating model
# Check that reminder has been changed
reminder = serializer.validated_data.get('reminder')
if reminder and reminder != instance.reminder: # Reminder is different
serializer.save(user=self.request.user,
reminder_user=self.request.user)
else:
serializer.save(user=self.request.user)
This solution will work depending on what type of reminder field is.
If it's String or Integer it will be ok. Problem is that if it's object. Viewset will raise error because serializer reminder field would be integer but instance.reminder would be instance of the reminder object so keep this in mind.

Django 1.3 CreateView, ModelForm and filtering fields by request.user

I am trying to filter a field on a ModelForm. I am subclassing the generic CreateView for my view. I found many references to my problem on the web, but the solutions do not seem to work (for me at least) with Django 1.3's class-based views.
Here are my models:
#models.py
class Subscriber(models.Model):
user = models.ForeignKey(User)
subscriber_list = models.ManyToManyField('SubscriberList')
....
class SubscriberList(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=70)
....
Here is my view:
#views.py
class SubscriberCreateView(AuthCreateView):
model = Subscriber
template_name = "forms/app.html"
form_class = SubscriberForm
success_url = "/app/subscribers/"
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
return super(SubscriberCreateView, self).form_valid(form)
Here is my original form for adding a Subscriber, with no filter:
#forms.py
class SubscriberForm(ModelForm):
class Meta:
model = Subscriber
exclude = ('user', 'facebook_id', 'twitter_id')
Here is my modified form, attempting to filter, but doesn't work:
#forms.py
class SubscriberForm(ModelForm):
class Meta:
model = Subscriber
exclude = ('user', 'facebook_id', 'twitter_id')
def __init__(self, user, **kwargs):
super(SubscriberForm, self).__init__(**kwargs)
self.fields['subscriber_list'].queryset = SubscriberList.objects.filter(user=user)
If I change this modified form as so:
def __init__(self, user=None, **kwargs)
It works - It brings me NO subscriber lists. But any way I try to pass the request user, I invariably get a a name "request" or name "self" not defined error.
So, how can I modify my code to filter subscriber_list by the request.user, and still use Django 1.3's CreateView.
I see you've been posting this question in various places.. and the way I found that is because I was trying to figure out the same thing. I think I just got it working, and here's what I did. I overwrote get_form() from FormMixin to filter a specific form fields queryset:
class MyCreateView(CreateView):
def get_form(self, form_class):
form = super(MyCreateView,self).get_form(form_class) #instantiate using parent
form.fields['my_list'].queryset = MyObject.objects.filter(user=self.request.user)
return form

How to subclass django's generic CreateView with initial data?

I'm trying to create a dialog which uses jquery's .load() function to slurp in a rendered django form. The .load function is passed the pk of the "alert" object. Also available in the class functions are things like self.request.user so I can pre-fill those fields, shown below in the Message model (models.py):
class Message(models.Model):
user = models.ForeignKey(User)
alert = models.ForeignKey(Alert)
date = models.DateTimeField()
message = models.TextField()
Subclassing django's CreateView makes it pretty easy to generate a context with an instance of the ModelForm (views.py):
class MessageDialogView(CreateView):
""" show html form fragment """
model = Message
template_name = "message.html"
def get_initial(self):
super(MessageDialogView, self).get_initial()
alert = Alert.objects.get(pk=self.request.POST.get("alert_id"))
user = self.request.user
self.initial = {"alert":alert.id, "user":user.id, "message":"test"}
return self.initial
def post(self, request, *args, **kwargs):
super(MessageDialogView, self).post(request, *args, **kwargs)
form_class = self.get_form_class()
form = self.get_form(form_class)
context = self.get_context_data(form=form)
return self.render_to_response(context)
The problem here is that self.initial does not get rendered with the form. I have insured that the form is indeed calling get_initial and the form instance has the proper initial data in post, but when the form is rendered in the template message.html it doesn't grab any of the initial data like I would expect. Is there a special trick to get this to work? I've scoured the docs (seems to be lacking examples on generic based class views) and source but I can't see what I'm missing.
get_initial() should just return a dictionary, not be bothered with setting self.initial.
Your method should look something like this:
def get_initial(self):
# Get the initial dictionary from the superclass method
initial = super(YourView, self).get_initial()
# Copy the dictionary so we don't accidentally change a mutable dict
initial = initial.copy()
initial['user'] = self.request.user.pk
# etc...
return initial
you can use like :
from django.shortcuts import HttpResponseRedirect
class PostCreateView(CreateView):
model = Post
fields = ('title', 'slug', 'content', 'category', 'image')
template_name = "create.html"
success_url = '/'
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())
that's work for me
(Edited because what you're trying does actually work)
I ran into the same problem yesterday, but it's working now – I think I was returning an object instead of a dict in get_initial.
In terms of fixing your problem, I'm a little suspicious of how much you seem to be doing in post() – could you try it with the default (non-overrided) post()?
You could also use pdb (or print statements) to check the value of self.get_form_kwargs make sure that initial is being set.

Resources