DRF error messages combine model and custom field error messages in response - django-rest-framework

I have this model in django:
product_name = models.CharField(max_length=20, blank=False, required = True)
code = models.CharField(max_length = 13, blank = False, required= True)
product_note = models.CharField(max_length = 2000, blank = True, required=False)
There is a custom validator and sanitizor for code which is applied in serializer. Now my issue is if both fields are incorrect (missing product_name and incorrect code), I get reply :"this field is required" for the product name and the code error message is never shown. If product_name is passed according to its model definitions though, the code error message is shown.
Is there a way both the model definition (i.e. required) and custom validation can be processed and returned at once?
from products.models import Product
from rest_framework import serializers
from validations.product_validators import valid_product_code
class NewProductSerializer(serializers.ModelSerializer):
def validate(self, data):
# validate data here
err_msg = {}
code = valid_product_code(data['code'])
if code == '':
err_msg['code']=['The entered product do not follow product code rules and could not be sanitized']
if err_msg:
raise serializers.ValidationError(err_msg)
data['code'] = code
return data
class Meta:
model = Product
fields = ('product_name', 'code','product_note')

if code fields is not empty serializer call validate_code() method.
class NewProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_name', 'code','product_note')
def validate_code(self, value):
# validate data here
err_msg = {}
code = valid_product_code(value)
if code == '':
err_msg['code']=['The entered product do not follow product code rules and could not be sanitized']
if err_msg:
raise serializers.ValidationError(err_msg)
return value

Related

Cannot generate post request for multiple data

I am trying to take input multiple data object in post request, but getting such error.
non_field_errors: [ Invalid data. Expected a dictionary, but got a list. ]
models.py
class OrderProduct(BaseModel):
product = models.ForeignKey(Product,on_delete=models.CASCADE)
order = models.ForeignKey(Order,on_delete=models.CASCADE)
order_product_price = models.FloatField(blank=False,null=False,default=0) # product may belong to offer do the price
order_product_qty = models.FloatField(default=1)
serializers.py
class OrderProductSerializer(serializers.ModelSerializer):
def update(self,instance,validated_data):
product = self.validated_data.pop('product')
order = self.validated_data.pop('order')
instance.orderproduct_qty =
self.validated_data.get('orderproduct_qty',instance.orderproduct_qty)
instance.product = product
instance.order = order
instance.save()
return instance
class Meta:
model = OrderProduct
fields = '__all__'
views.py
def post(self,request,*args,**kwargs):
if request.data['contact_number'] == '':
request.POST._mutable =True
request.data['contact_number'] = request.user.mobile_number
request.POST._mutable = False
serializer = OrderSerializer(data=request.data,many=isinstance(request.data,list),context={'request': request})
print(serializer)
if serializer.is_valid():
serializer.save(user = request.user,created_by = request.user)
return Response(serializer.data,status=status.HTTP_200_OK)
else:
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
urls.py
path('orderproduct/',views.OrderProductList.as_view()),
When you call serializer.save(). It's only perform create() action which is only create one and accept dictionary data type only. If you want to save multiple data like that, you will have to override the create function of the serializer class. You can do something similar like this or run a for loop.
serializers.py
def create(self, validate_data):
# Get the data objects you need to perform bulk create
order_products = OrderProduct.objects.bulk_create(validate_data)
return order_products
views.py
if serializer.is_valid(raise_exception=True):
# Replace the serializer.save() by this line to trigger the create method in serializer
self.perform_create(serializer)
return Response(...)

How to search with django_tables2 when using filters

My problem is that I can not take back any data after filtering.
My search fields are : id name and surname. The two last taken from the Client which is foreign key.
filters.py
class OrderFilter(django_filters.FilterSet):
client__name = django_filters.CharFilter(lookup_expr='icontains')
client__surname = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Order
fields = ['id']
models.py
class Order(models.Model):
client = models.ForeignKey(Client,verbose_name=u'Client')
tables.py
class OrderTable(tables.Table):
#CUSTOM COLUMN EXAMPLE
order_id=tables.Column(verbose_name= 'ID Order',orderable=False,empty_values=())
class Meta:
#define the model
model = Order
exclude=('id')
template_name = 'django_tables2/bootstrap.html'
sequence = ("order_id")
views.py
class OrderListView(SingleTableMixin,FilterView):
table_class = OrderTable
model = Order
template_name='orders/orde_list.html'
filterset_class = OrderFilter
def get_context_data(self, **kwargs):
context = super(OrderListView, self).get_context_data(**kwargs)
##filter the orders of the past 4 months.
last_four_months=date.today() - timedelta(120)
object_list=Order.objects.filter(order_created__gte=last_four_months,ays=1).order_by('-invoice_date')
table=self.table_class(object_list)
RequestConfig(self.request).configure(table)
context['table'] = table
#clear all fields
has_filter = any(field in self.request.GET for field in set(self.filterset_class.get_fields()))
context['has_filter'] = has_filter
return context
I noticed that when I create a custom queryset to fill my table, for ex: object_list=Order.objects.filter(order_created__gte=last_four_months).order_by('-invoice_date') the filtering is not working.
When I put nothing it works properly.
Any idea why this happening?
The issue was that I have not defined that my custom queryset must be inserted in the custom table(tables.py) and more important to be filtered by the custom filter (filters.py).
After doing that the custom queryset filtered correctly.
class OrderAYSListView(LoginRequiredMixin,SingleTableMixin,FilterView):
login_url = '/login/'
table_class = OrderTable2
model = Order
template_name='orders/order_ays_list.html'
filterset_class = OrderFilter
def get_context_data(self, **kwargs):
context = super(OrderAYSListView, self).get_context_data(**kwargs)
##filter the orders of the past 4 months.
last_four_months=date.today() - timedelta(120)
object_list=Order.objects.filter(order_created__gte=last_four_months,ays=1).order_by('-invoice_date')
f = self.filterset_class(self.request.GET, queryset=object_list)
context['filter'] = f
table = self.table_class(f.qs)
RequestConfig(self.request).configure(table)
context['table'] = table
#clear all fields
has_filter = any(field in self.request.GET for field in set(self.filterset_class.get_fields()))
context['has_filter'] = has_filter
return context

How to remove validator on id field in wtforms

I have created sqlalchemy class and wtform Form from class.
from wtforms.ext.sqlalchemy.orm import model_form
class ClientGroup(Base, BaseModel):
__tablename__ = 'client_groups'
id = Column(Integer, primary_key=True)
name = Column(String(255))
sale = Column(Integer)
def __unicode__(self):
return self.name
ClientGroupForm = model_form(ClientGroup, Form, field_args={
'name': {'validators' : [validators.Required(), validators.Length(min=5)]},
})
in app I have following code:
form = ClientGroupForm(request.form)
if form.validate():
.. save data
When i send a new data to app its returns me validation error. ID must be an integer field.
But a new data hasn't any id.
Please, give me advice How can I use wtforms with sqlalchemy?
Make it optional explicitly, by adding the following to the field_args:
'id': {'validators' : [validators.Optional()],
However, I think you should exclude the id field from the form completely:
ClientGroupForm = model_form(ClientGroup, Form, field_args={
'name': {'validators' : [validators.Required(), validators.Length(min=5)]},
exclude=['id'],
})

Django: Displaying a form from one model with content from a second model on one page view

I have an irksome little problem with a forum that I am building.
I need to generate a page that contains a form to populate one
model but that page should also display an entry from another related model.
I want the form to populate a new response in the model Responses (see code below).
That model has the model StartMsg as a foreign key. I want the page view (response_form) to display StartMsg.msg that the user is responding to. The problem is that I am using django's built in forms to generate the form and render_to_response to call the page. The render_to_response statement (marked (A)) sends a dictionary containing the form components from the Responses model to the html template.
How do I include info about the model StartMsg into the render_to_response statement (marked
with (A), below)? Is there a better way to accomplish what I am after?
Here are the models:
class StartMsg (models.Model):
msg_title = models.TextField(max_length=500)
msg = models.TextField(max_length=2000)
pub_date = models.DateTimeField('date published')
author = models.ForeignKey(User)
def __unicode__(self):
return self.msg
class Responses (models.Model):
startmsg = models.ForeignKey(StartMsg) #one startmsg can have many responses
response = models.TextField()
responder = models.ForeignKey(User)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.response
Below is the form processing function followed by the form model.
def response_form (request, msg_id):
msg = get_object_or_404(StartMsg, pk=msg_id)
form = MsgRespForm(request.POST or None)
if form.is_valid():
new_rspns = form.save(commit =False)
#retrieve StartMsg entry to assign to the response entry foreign key
message = StartMsg.objects.get(pk=msg_id)
new_rspns.startmsg = message
response = form.cleaned_data['response']
new_rspns.response = response
new_rspns.responder= request.user.username()
new_rspns.pub_date = datetime.now()
new_rspns.save()
return HttpResponseRedirect(reverse('forum.views.forum', )) #if form is processed, view goes here
return render_to_response( #if form not processed, view goes here
'forumresponseform.html',
{'form': form}, (A)
context_instance = RequestContext(request)
)
class MsgRespForm(forms.ModelForm):
# Add Labels to form fields:
response = forms.CharField(label='Your Response',
widget=forms.Textarea(attrs={'cols': 60, 'rows': 10}))
class Meta: #Define what fields in the form
model = Responses
fields = ('response',)
I found a solution that seems to work.
You can make a function that builds a dictionary out of a variable number of model queries then pass that dictionary to the form template on line (A).
def build_dict(request, ** kwargs):
d = dict(user=request.user, ** kwargs)
return d
msg = get_object_or_404(StartMsg, pk=msg_id)
form = MsgRespForm(request.POST or None)
dict = build_dict(request, msg=msg, form=form)
return render_to_response( #if form not processed, view goes here
'forumresponseform.html',
{'form': dict}, (A)
context_instance = RequestContext(request)
)

Django Ajax field help

I have a Django application where I'm trying to have a form that populates a drop down dynamically based on a previous dropdown.
simplified Models:
class VehicleMake(models.Model):
make = models.CharField(max_length = 30)
class VehicleModel(models.Model):
model = models.CharField(max_length = 80)
make = models.ForeignKey(VehicleMake)
class Listing(models.Model):
make = models.ForeignKey(VehicleMake)
model = models.ForeignKey(VehicleModel)
Form:
class DynamicChoiceField(ModelChoiceField):
def clean(self, value):
return value
class MyForm(ModelForm):
category = ModelChoiceField(VehicleCategory.objects, widget=forms.Select(attrs={'onchange':'FilterMakes();'}))
make = DynamicChoiceField(VehicleMake.objects,widget=forms.Select(attrs={'disabled':'true','onchange':'FilterModels();'}), empty_label="Select Model")
model = DynamicChoiceField(VehicleModel.objects,widget=forms.Select(attrs={'disabled':'true'}), empty_label="Select Make")
class Meta:
model = Listing
View:
def new_listing(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
form.save()
else:
form = MyForm()
return render_to_response("newlisting.html", {
"form": form,'model_id':model_id,'make_id':make_id
})
I also have some ajax defined for the auto-populate but this is not the problem
When I submit the form I get the following:
Cannot assign "u'2'": "Listing.make" must be a "VehicleMake" instance.
if I try
make=VehicleMake.objects.get(pk=request.POST['make'])
form.fields['make'] = make
then I get
'VehicleMake' object has no attribute 'widget'
After the suggestion of one of the commenter's that the DynamicChoiceField class was the culprit I removed it and set the form objects for ModelChoiceFields with the exact same other parameters. The object appears to pass and validate correctly as well. The extra class existed based on an old tutorial I found. it appears that what the author did there works with the forms.ChoiceField but is not required for using a ModelChoiceField
thanks everyone for the help

Resources