Is there a converter between django.forms.Form and rest_framework.serializers? - django

I already have plenty of forms defined in my django 1.9 project. Now I need to export them as REST (DRF 3.5.3) as well.
With just a bit of hacking, I was able to provide GET and PUT methods. But I also need to provide the OPTIONS method and I can't seem to find anything that would help me do that.
So, is there something that would convert an instanced form to DRF Serializer / ViewSet?

No but you can do the other way around as explained in http://www.django-rest-framework.org/topics/html-and-forms/#rendering-forms.

drf-braces can do this.
from django import forms
from drf_braces.serializers.form_serializer import FormSerializer
class MyForm(forms.Form):
foo = forms.CharField(max_length=32)
bar = forms.DateTimeField()
class MySerializer(FormSerializer):
class Meta(object):
form = MyForm
https://github.com/dealertrack/django-rest-framework-braces/blob/master/docs/overview.rst

I didn't want to mess with all of my custom logic in the forms, so I just added an as_options method:
class APIViewForm(Form):
def as_dict(self):
return {fld.auto_id: self.initial.get(fld.name, None) for fld in self}
def as_options(self):
flds = {}
for fld in self:
flds[fld.name] = f = {}
fld = fld.field
f.update(dict(
required=fld.required,
read_only=fld.disabled,
label=fld.label,
))
if isinstance(fld, (CharField, URLField)):
f['type'] = 'field'
if fld.max_length:
f['max_length'] = fld.max_length
if fld.min_length:
f['min_length'] = fld.min_length
elif isinstance(fld, IntegerField):
f['type'] = 'integer'
if fld.max_value:
f['max_value'] = fld.max_value
if fld.min_value:
f['min_value'] = fld.min_value
elif isinstance(fld, ChoiceField):
f['type'] = 'choice'
f['choices'] = [dict(value=c[0], display_name=c[1]) for c in fld.choices]
elif isinstance(fld, DateTimeField):
f['type'] = 'datetime'
return dict(
name=self.__class__.__qualname__,
description='',
renders=['application/json'],
parses=['application/json'],
actions=dict(PUT=flds)
)
It seems to do the trick

Related

How to modify the form filed data?

I have a model:
class PastStudy(Model):
grade_average = FloatField(null=True)
I have a modelform as below:
class PastStudyForm(ModelForm):
class Meta:
model = PastStudy
fields = ('grade_average', )
What I have in view:
...
if request.POST:
past_study_form = PastStudyForm(request.POST)
if past_study_form.is_valid():
return HttpResponse(past_study_form.cleaned_data['grade_average'])
else:
profile_filter_past_study_form = ProfileFilterPastStudyForm()
...
What I need is that I want to write a clean method for PastStudyForm so that in case I entered 90 as grade average, HttpResponse converts it two 0-20 grading scheme and returns 18.
I tried this and I was still getting 90 not 18
class PastStudyForm(ModelForm):
class Meta:
model = PastStudy
fields = ('grade_average', )
def clean(self):
cleaned_data = super().clean()
grade_average = self.cleaned_data['grade_average']
self.cleaned_data['grade_average'] = grade_average/5
return cleaned_data
and This:
class PastStudyForm(ModelForm):
class Meta:
model = PastStudy
fields = ('grade_average', )
def clean_grade_average(self):
grade_average = self.cleaned_data['grade_average']
data = grade_average/5
return data
However, I still get 90. I also have tried a few other methods but I still was getting 90 in HttpResponse
Maybe using clean method be wrong and I should do something else!
The real code is huge and I have summarized it in here and other aspects of the problem are not explained in here. That is why I prefer and expect to get a response in which I am advised how to it in the form definition, not other methods such as converting it in the view.
in your clean method, you assign the result of your calculation method into self.cleaned_data,
while you are returning cleaned_data not self.cleaned_data.
it is different variable.
try this instead:
self.cleaned_data = super().clean()
grade_average = self.cleaned_data['grade_average']
self.cleaned_data['grade_average'] = grade_average/5
return self.cleaned_data

Replacement for django `render_options`

So I am implementing this answer: Country/State/City dropdown menus inside the Django admin inline, but the def render piece of code needs to be redone.... I have managed to redo it, but I am struggling to find a replacement (or the correct code) for the self.render_options method (which was deprecated on 1.11) of the Widget class.
I am on Django 2.1.
What should I change?
Here is my code:
class StateChoiceWidget(widgets.Select):
def render(self, name, value, attrs=None, renderer=None):
self.choices = [(u"", u"---------")]
if value is None:
value = ''
model_obj = self.form_instance.instance
if model_obj and model_obj.country:
for m in model_obj.country.state_set.all():
self.choices.append((m.id, smart_text(m)))
else:
obj = State.objects.get(id=value)
for m in State.objects.filter(country=obj.country):
self.choices.append((m.id, smart_text(m)))
final_attrs = self.build_attrs(attrs)
output = ['<select%s>' % flatatt(final_attrs)]
for option in self.choices:
output.append('<option value="%s">%s</option>' % (option[0], option[1]))
output.append('</select>')
return mark_safe(''.join(output))
Original poster updated the sample code, so now it doesn't show the code in the question: see previous revision https://stackoverflow.com/revisions/52174508/1
So I figured out the answer. Will post it here in case someone runs into the same issue.
class StateChoiceWidget(widgets.Select):
def render(self, name, value, attrs=None, renderer=None):
self.choices = [(u"", u"---------")]
if value is None or value == '':
value = ''
model_obj = self.form_instance.instance
if model_obj and model_obj.country:
for m in model_obj.country.state_set.all():
self.choices.append((m.id, smart_text(m)))
else:
obj = State.objects.get(id=value)
for m in State.objects.filter(country=obj.country):
self.choices.append((m.id, smart_text(m)))
final_attrs = self.build_attrs(attrs)
s = widgets.Select(choices=self.choices)
select_html = s.render(name=name,value=value,attrs=attrs)
return mark_safe(''.join(select_html))

Call method on Django fields

class Colour(models):
...
def colour_preview(self):
return format_html(...)
class ColourTheme(models):
colour1 = models.ForeignKey(Colour)
colour2 = models.ForeignKey(Colour)
colour3 = models.ForeignKey(Colour)
...
def preview(self):
for field in self._meta.get_fields(include_parents=False):
if (field.related_model == Colour):
field.colour_preview()
I have a ColourTheme model with multiple Colour foreign keys, and I want to call a function on each of the Colour objects referred to by those fields. The last line of code above fails. I would like to call colour_preview on all Colour fields without hardcoding them (self.colour1.colour_preview() works but not ideal).
How might I achieve this?
You cannot refer to the field in order to access related object method.
Try something like this (I haven't tested it):
class ColourTheme(models):
colour1 = models.ForeignKey(Colour)
colour2 = models.ForeignKey(Colour)
colour3 = models.ForeignKey(Colour)
...
def preview(self):
for field in self._meta.get_fields(include_parents=False):
if (field.related_model == Colour):
field_obj = field.value_from_obj(self) # To get obj reference
field_obj.colour_preview()

Is it dangerous to define __contains__ on a metaclass?

I'm writing a custom EnumMeta class in Python 2.7 that will collect enum keys and values from some class and augment that class with some additional fields.
class EnumMeta(type):
def __init__(cls, name, bases, props):
cls.__all_values__ = [...] # collect all interesting properties
def __contains__(cls, value):
return value in cls.__all_values__
class TheFellowshipOfTheRing(object):
__metaclass__ = EnumMeta
FRODO = 'Frodo Baggins'
SAM = 'Samwise "Sam" Gamgee'
MERRY = 'Meriadoc "Merry" Brandybuck'
PIPPIN = 'Peregrin "Pippin" Took'
GANDALF = 'Gandalf the Grey'
ARAGORN = 'Aragorn (Strider)'
LEGOLAS = 'Legolas'
GIMLI = 'Gimli'
BOROMIR = 'Boromir'
print 'Gandalf the Grey' in TheFellowshipOfTheRing
# True
print 'Saruman' in TheFellowshipOfTheRing
# False
I'm wondering if implementing container-specific functions, such as __contains__, on a metaclass is a dangerous thing to do, and if so, why?

filter using Q object with dynamic from user?

In my views.py I have a method:
#......
def get_filter_result(self, customer_type, tag_selected):
list_customer_filter=[]
customers_filter = Customer.objects.filter(Q(type__name=customer_type),
Q(active=True),
Q(tag__id=tag_selected))
for customer_filter in customers_filter:
customer_filter.list_authorize_sale_type = sale_type_selected(customer_filter.authorize_sale_type)
list_customer_filter.append(customer_filter)
return list_customer_filter
**My case tag_selected is the checkbox values that user checked
I have a problems with tag_selected(is the list=1,2,3,...) that pass from my url
/?customer_type=TDO&tag=2 ===>filter okay
/?customer_type=TDO&tag=3 ===>filter okay
?customer_type=TDO&tag=2,3 ===>How Can I add And condition in filter?
for example
if len(tag_selected)==1:
customers_filter = Customer.objects.filter(Q(type__name=customer_type),
Q(active=True),
Q(tag__id=tag_selected))
else:
customers_filter = Customer.objects.filter(Q(type__name=customer_type),
Q(active=True),
Q(tag__id=tag_selected[0])
Q(tag__id=tag_selected[1])
Q(tag__id=tag_selected[2])
...
)
This works for both single and multiple conditions:
idseq = request.POST['tag'].split(',')
tag_qs = reduce(operator.or_, (Q(tag__id=x) for x in idseq))
Customers.objects.filter(..., tag_qs)