How to add a custom attribute to the hidden id field from a formset modelform - attr

I'm trying to add a "form" attribute to the modelform used by a formset so that I can put my form elements into a table as described here Form inside a table
I found this answer which seems to be what I wanted How to add class, id, placeholder attributes to a field in django model forms
I've implemented this as shown in my forms.py
class EditAssemblyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditAssemblyForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update(
{'form': 'EditAssemblyForm'})
self.fields['issue'].required = False
self.fields['qty'].widget.attrs['min'] = 1
#self.fields['id'].widget.attrs.update({'form': 'EditAssemblyForm'})
class Meta:
model = Relationship
fields = ['issue', 'qty']
This is working for the issue and qty form elements but not for the hidden id field. I tried to specifically apply this to self.fields['id'] as can be seen in the commented line but this causes an error:
Exception Type: KeyError
Exception Value: 'id'
Exception Location: C:\SvnDeltaRepository\DeltaSoftware\django\delta\PaNDa\forms.py, line 98, in __init__

I ended up adding it in the views.py instead:
for form in formset:
form.fields.get('id').widget.attrs['form'] = 'EditAssemblyForm'
Seems to work
--edit--
Also had to add:
for field in formset.management_form.fields:
formset.management_form.fields[field].widget.attrs['form'] = 'EditAssemblyForm'
This adds the same attribute to the hidden fields that the management_form generates. Without this the formset.is_valid() would fail.

Related

Django REST serializer required field

I have a simple serializer with one required field:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ('field1', 'field2')
In my model there is an 'url' field which is required to create new object (method: POST). I would like to set required: False for PUT method. How can I achieve that? Thanks for any clues...
I assume you want to change/set one or multiple fields of an existing MyModel instance.
In such case, you need to pass a partial=True keyword argument to serializer. Then even if you PUT or PATCH without url field in data, your serializer.is_valid() would evaluate to True.
https://www.agiliq.com/blog/2019/04/drf-polls/#edit-a-poll-question should help if my assumption about your question is correct.
I found this answer helpful: Django Rest Framework set a field read_only after record is created .
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance is not None:
self.fields.get('url').read_only = True
This code works fine.

DRF How to patch when file upload is required...?

DRF How do you patch when file upload is required and you don't want to post the file again?
I keep getting this response: {'xyz_file': [u'No file was submitted.']}
I don't have xyz_file required on the serializer. This is not a field on the model because I don't want to save it in the db.
class XYZSerializer(ParentSerializer):
xyz_file = serializers.FileField(source='get_file_field', use_url=False, validators=[xyz_extensions_validator])
class Meta:
model = models.XYZModel
fields = ('name', 'xyz_file', 'active',)
I've tried overwriting the update method in the view and serializer. Neither worked.
Ok so this is how i fixed my problem.
In my serializer I added this method:
def exclude_fields(self, fields_to_exclude=None):
if isinstance(fields_to_exclude, list):
for f in fields_to_exclude:
f in self.fields.fields and self.fields.fields.pop(f) or next()
In my viewset I overrode the update method with this:
def update(self, request, *args, **kwargs):
partial = False
if 'PATCH' in request.method:
partial = True
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
if 'xyz_file' in request.data and not request.data['xyz_file']:
serializer.exclude_fields(['xyz_file'])
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.data)
So the idea is to remove the field from even being validated. Also if you wanted to run this on a field that is on the model, the popping of the field will prevent you from saving the non validated field.
If you are using PATCH HTTP method, then you could turn on partial updates, which doesnt require any fields I believe.
Then you define your serializer inside your update method in your view:
serializer = XYZSerializer(instance=xyz,data=request.data,partial=True)
Is written here http://www.django-rest-framework.org/api-guide/serializers/#partial-updates.

None-value in browseable API

In Django REST Framework, given a field in a Django model which has null=True and blank=True, and where the field is required=False in the serializer, is there any way to have the browseable API render the HTML dropdown with a blank value? Using JSON it is possible to set the field to null, I just can't figure out how to also allow this through the HTML form.
Said in pictures:
I currently have:
I want to get to:
Found it. Simplified:
class SwitchPortSerializer(serializers.HyperlinkedModelSerializer):
def get_fields(self, *args, **kwargs):
fields = super(SwitchPortSerializer, self).get_fields(*args, **kwargs)
fields['switch'].empty_label = ''
return fields

dynamic form requirements from model

I'm trying to generate a dynamic registration form, based on on specific client needs. I've created a UserProfile model with most of the fields set as blank=True.
When the form gets generated, I pull the client specified fields from another db table and generate the registration form. All this works, except that all the fields allow blank values. So far I have this
def RegProfileForm(include_list, *args, **kwargs):
class ProfileForm(forms.ModelForm):
class Meta:
model = hr.UserProfile
fields = include_list
def __init__(self):
super(ProfileForm, self).__init__(*args, **kwargs)
return ProfileForm()
Then I call this form like this:
includes = ['gender','work_phone'] # dynamic fields
of = RegProfileForm(includes)
How do I dynamically remove the blank=True requirement from certain specified fields during runtime, or when I generate the form?
I figured out I can just do something like this to override the default values from the model:
form = MyAuthForm(data)
form.fields['first_name'].required = True
form.fields['email'].required = False

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