context_processor.py:
def max_min(request):
"""
some code. request required, because I need
calculate some information about each user
"""
return {'max': max, 'min':max}
forms.py:
class MaxMin(forms.Form):
my_field = forms.IntegerField(
min_value=min, max_value=max, required=True,
widget=forms.NumberInput(attrs={'class':'form-control',
'required': 'true'}))
So, I have code in context_processor.py (it uses on all pages of site), that I want use in my forms.py. But I dont know, how to import and use it. max_min needs request, but in forms.py I dont have it.
Yes, I know, I can use {{min}} and {{max}} in forms html, without forms.py, but then I need to check in code, whats values I got through POST.
Thanks.
UPDATE:
class MAxMin(forms.Form):
def __init__(self, req, *args, **kwargs):
super(MAxMin, self).__init__(*args, **kwargs)
self.request = req
def mymethod(self):
return max_min(self.request)
my_field = forms.IntegerField(
min_value=mymethod(), required=True,
widget=forms.NumberInput(attrs={'class':'form-control',
'required': 'true'}))
What about injecting request at the time of instantiation?
#views.py
form = MyForm(request)
#forms.py
def __init__(self, req, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.request = req
#somewhere in your form's body
max_min(self.request)
UPDATE
Well, your situation appears to be different from what I've expected.
Consider this approach:
class MAxMin(forms.Form):
my_field = forms.IntegerField(required=True,
widget=forms.NumberInput(attrs={'class':'form-control',
'required': 'true'}))
def __init__(self, req, *args, **kwargs):
super(MAxMin, self).__init__(*args, **kwargs)
self.fields['my_field'].min_value = max_min(req)
Related
Python 3.5.2
Could you help me understand how parameters are transmitted to the method.
Just in case: this is Django but the question seems to be just about Python.
As far as I can catch from the Django's documentation: if I pass form_kwargs to a formset like this, several forms will be created. And in the form I expect to catch the parameter in **kwargs.
class MyArticleForm(ArticleForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(MyArticleForm, self).__init__(*args, **kwargs)
formset = ArticleFormSet(form_kwargs={'user': user})
Well, this is not working:
File "/home/michael/workspace/formsets/general/forms.py", line 15, in __init__
self.user = kwargs.pop('user')
KeyError: 'user'
If I step in the debugger: kwargs definitely doesn't contain any 'user'. And args is an empty tuple.
But this works:
class MyArticleForm(ArticleForm):
def __init__(self, user=None, *args, **kwargs):
pass
And this works:
class MyArticleForm(ArticleForm):
def __init__(self, user, *args, **kwargs):
pass
Well, I can't understand how can I catch this parameter in using kwargs?
///////////
ADDED LATER
ArticleFormSet = formset_factory(MyArticleForm)
def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
can_delete=False, max_num=None, validate_max=False,
min_num=None, validate_min=False):
pass
class BaseFormSet(object):
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, form_kwargs=None):
...
self.form_kwargs = form_kwargs or {}
....
I'm using the ModelFormSetView class in django-extra-views to create a formset view of all WorkerStatus entries connected to a Worker. I'd also like to use custom validation on the formset, so I've defined my own formset_class and form_class in the view. Here's the view definition:
class WorkerStatusUpdateView(ModelFormSetView):
model = WorkerStatusEntry
formset_class = WorkerStatusFormSet
form_class = WorkerStatusForm
template_name = 'staff/workers/worker_status_update.tmpl'
can_delete = True
can_order = False
fields = ['status', 'start_date']
def dispatch(self, request, *args, **kwargs):
self.worker = Worker.objects.get(pk=self.kwargs['worker_pk'])
return super(WorkerStatusUpdateView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
ctx = super(WorkerStatusUpdateView, self).get_context_data(*args, **kwargs)
ctx['worker'] = self.worker
return ctx
def get_queryset(self, *args, **kwargs):
return self.worker.statuses.all()
...and here are the definitions of the form and formset respectively:
class WorkerStatusForm(forms.ModelForm):
class Meta:
model = WorkerStatusEntry
fields = ['status', 'start_date']
class WorkerStatusFormSet(BaseModelFormSet):
class Meta:
model = WorkerStatusEntry
def __init__(self, queryset, *args, **kwargs):
super(WorkerStatusFormSet, self).__init__(*args, **kwargs)
def clean(self):
print "Cleaning"
This results in a page where EVERY WorkerStatusEntry in the database is shown in the formset, regardless of get_queryset(). One thing you'll notice is that WorkerStatusFormSet.__init__ takes a queryset argument: I put that there because there was a queryset argument passed to it from the ModelFormSetView, but I don't know what to do with it.
Another thing to note: if I take formset_class = WorkerStatusFormSet out of the view definition, the correct queryset shows up in the formset. However I need to use my own formset class to validate across the whole formset. Unless there's another way?
The problem is your WorkerStatusFormSet.__init__ method. Looking at the code for BaseModelFormSet, the __init__ method already takes a queryset parameter. Since you aren't doing anything in your __init__ method except calling super(), the easiest fix is to remove it.
It's not a good idea to change the signature of the __init__ method as you have done for two reasons
def __init__(self, queryset, *args, **kwargs):
super(WorkerStatusFormSet, self).__init__(*args, **kwargs)
You have changed the order of the arguments. If you look at the code for BaseModelFormset, the first argument is data. That means that data might be incorrectly assigned to queryset if somebody calls WorkerStatusFormSet(data, ...)
You do not do anything with queryset or pass it to super(), so it is lost.
I have a Django form and l would like to pass a user instance when the form is created
First Approach
This is where l create the form and pass the instance of the user:
form = QuestionForm(request.user, request.POST)
And inside the QuestionForm
def __init__(self, user, *args, **kwargs):
super(QuestionForm, self).__init__(*args, **kwargs)
self.data = user
log.info(self)
Study.objects.filter(owner = self.data.id))
Second Approach
This is where l create the form and pass the request:
form = QuestionForm ( ..., request=request)
And inside the QuestionForm
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(MyForm, self).__init__(*args, **kwargs)
ref = forms.ModelChoiceField(queryset=Study.objects.filter(owner = self.request.user.id))
Now l am getting an error that self is not define and as such l cannot get the user id to query the Study class
Any help would be much appreciated
If you do this code in field declaration section like
class QuestionForm(forms.Form):
ref = forms.ModelChoiceField(queryset=Study.objects.filter(owner=...)
then it will not work because it still doesn't have self variable.
You can do this in init method like this
class QuestionForm(forms.Form):
ref = forms.ModelChoiceField()
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['ref'].queryset = Study.objects.filter(owner=request.user)
Trying to pass in a variable to help with the queryset that ModelChoiceField requires. Getting error TypeError: __init__() takes at least 2 arguments (1 given) and I'm not sure why. See code below.
forms.py
class uploadForm(forms.Form):
def __init__(self, trainer, *args, **kwargs):
super(uploadForm, self).__init__(trainer, *args, **kwargs)
self.fields["client"] = forms.ModelChoiceField(
queryset=Trainee.objects.filter(trainer=trainer),
widget=forms.Select(attrs={'class': 'signup-form-input'})
)
views.py
uploadForm = uploadForm(trainer)
You are getting this exception because following code line is wrong:
super(uploadForm, self).__init__(trainer, *args, **kwargs)
In init method. It should be just
super(uploadForm, self).__init__(*args, **kwargs)
as in super class's constructor trainer is not an argument.
Anyways, the way you are doing is wrong! you should implement your form class as below:
forms.py:
class UploadForm(forms.Form):
def __init__(self, *args, **kwargs):
super(UploadForm, self).__init__(*args, **kwargs)
self.fields["client"] = forms.ModelChoiceField(
queryset=Trainee.objects.filter(trainer=kwargs['trainer']),
widget=forms.Select(
attrs={
'class': 'signup-form-input'
}
))
views.py:
uploadform = UploadForm(trainer=trainer)
One more note: If trainer is not a field in your form then popup trainer before to call super class constructor as:
class UploadForm(forms.Form):
def __init__(self, *args, **kwargs):
trainer = kwargs.pop('trainer', None)
super(UploadForm, self).__init__(*args, **kwargs)
self.fields["client"] = forms.ModelChoiceField(
queryset=Trainee.objects.filter(trainer=trainer),
widget=forms.Select(
attrs={
'class': 'signup-form-input'
}
))
views.py is as I given in my answer.
I am using Django REST Framework to create an API for my web app. I have a class 'Comment', that has depth=2 set in the Meta class. This works great when GETing the Comments. When I try to send a POST or PUT request though (i.e. create a new Comment) I am told I need to include objects instead of ForeignKey IDs.
Here's my Serializer class:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
depth = 2
The model:
class Comment(models.Model):
user = models.ForeignKey(User, null=True, blank=True,
related_name='comments')
budget = models.ForeignKey(Budget, related_name='comments')
published = models.BooleanField(default=False)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
The view code:
class Comments(generics.ListCreateAPIView):
model = Comment
serializer_class = CommentSerializer
def pre_save(self, obj):
obj.user = self.request.user
And the error that is displayed in the output (JSON) is:
{"user": ["This field is required."], "budget": [{"non_field_errors": ["Invalid data"]}]}
When this raw data is sent:
{"budget": 2, "published": true, "body": "Another comment"}
I know this is a little bit late but I ended up using 2 serializers like so:
class CommentReadSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
depth = 2
class CommentWriteSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
Then used like this:
class Comments(generics.ListCreateAPIView):
model = Comment
serializer_class = CommentReadSerializer
def create(self, request, *args, **kwargs):
serializer = CommentWriteSerializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
self.pre_save(serializer.object)
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
headers = self.get_success_headers(serializer.data)
serializer = CommentReadSerializer(serializer.object)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You can set different serializers by overriding the get_serializer_class() function, like so:
def get_serializer_class(self):
method = self.request.method
if method == 'PUT' or method == 'POST':
return YourWriteSerializer
else:
return YourReadSerializer
I thought to add this one, since i came here from Googling after a while.
I believe the proper way to define a serializer field that refers to a foreign key relationship is through something like serializers.PrimaryKeyRelatedField. I don't believe that model serializers automatically use this field class without defining it explicitly in the serializer class.
http://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield
I would imagine that a PrimaryKeyRelatedField serializer would correctly handle JSON data submissions like the one you used in your example.
I had the same problem so I Solved making custom generic methods.This is better implementation of above answers
class CustomListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get_serializer_class(self):
method = self.request.method
if method == 'PUT' or method == 'POST':
return self.writeSerializers
else:
return self.readSerializers
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Similarily RUD,
class CustomRetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
"""
Concrete view for retrieving, updating or deleting a model instance.
"""
def get_serializer_class(self):
method = self.request.method
if method == 'PUT' or method == 'POST':
return self.writeSerializers
else:
return self.readSerializers
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) # enter code here
Now I just give writeSerializers and readSerializers values in Views.py
Also to create Read-write Serializers there is an easy way.
class employeeWriteSerializer(serializers.ModelSerializer):
class Meta:
model = employee
fields = ('username','email',..)
class employeeReadSerializer(serializers.ModelSerializer):
class Meta(employeeWriteSerializer.Meta):
depth = 1
It saves time and repetitive work you can also add authentication classes in custom generic Api(Retitve work). Thanks.