How do I make integers optional in DRF - python

I have a serializer with an integer field
foo = serializers.IntegerField()
and I'd like that field to be optional. It seems obvious to me that
foo = serializers.IntegerField(required=False)
should work, but it doesn't, I get the error message:
{"error":{"foo":["A valid integer is required."]
I though I said that it wasn't required. I also tried adding a default,
serializers.IntegerField(required=False, default=42)
Am I missing something? Is this even possible?

While using 'required = False'
Normally an error will be raised if a field is not supplied during
deserialization.
Setting this to False also allows the object attribute or dictionary key
to be omitted from output when serializing the instance.
you should try setting: 'null=True'
Normally an error will be raised if None is passed to a serializer field. Set this keyword argument to True if None should be considered a valid value.
Defaults to False
for further reading DRF docs

Related

Update a field of a Django Object

I'm trying to update an object field in django. Usually I would do something like this:
# MODEL ---
class MyObj(models.model):
name: models.CharField(max_length=10)
surname: models.CharField(max_length=10)
# VIEW ---
# [...]
myObj = MyObj.objects.get(pk=1)
myObj.name = 'John'
myObj.save()
The problem is that the field to modify (in the example above: "name") is not known and passed as an argument to the post request. So I would have something like this:
# VIEW ---
# [...]
field = self.request.query_params['field_to_modify']
myObj = MyObj.objects.get(pk=1)
myObj[field] = 'John'
myObj.save()
now this triggers the error:
myObj[field] = 'John'
TypeError: 'MyObj' object does not support item assignment
What is the correct way to update an "unknown" field of a django object?
UPDATE
Thank you for your answers so far! OK so the way to go is apparently using setattr (as per the answers). Now the problem is that it does not allow me to save the modified object without checking if it is valid.
So I tried using the Serializer to check the object validity but is not working:
field = self.request.query_params['field_to_modify']
myObj = MyObj.objects.get(pk=1)
setattr(myObj, field, 'John')
serial = MyObjSerializer(myObj)
serial.is_valid(raise_exception=True)
serial.save()
error:
AssertionError: Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.
You're looking for setattr to set an attribute by name.
field = self.request.query_params['field_to_modify']
# TODO: add validation for `field`
myObj = MyObj.objects.get(pk=1)
setattr(myObj, field, 'John')
myObj.save(update_fields=[field]) # no need to update anything but the single field
Of course, this won't let you automatically attach any arbitrary data to models and expect it to be saved; if you need something like that, maybe use a JSON field.
EDIT:
For using a Django REST Framework serializer (which, cough, wasn't part of the original question or tags, but could be inferred from query_params), one could do
field = self.request.query_params['field_to_modify']
myObj = MyObj.objects.get(pk=1)
serial = MyObjSerializer(instance=myObj, data={field: 'John'})
serial.is_valid(raise_exception=True)
serial.save()
Yeah it looks like Django model instances do not support that operation. It looks like you can use setattr though from looking at this previous question / answer. Update model instance with dynamic field names

Does 'default=...' in Django model fields set 'blank=True' by default?

I have a question for the Django model field when applying the default setting. My problem is as follows:
# Model description
class aa(models.Model):
a = models.FloatField(default=1)
b = models.FloatField()
When inserting an entry into the database, I apply the following functions to do the validation without any error:
data = {'b': 1.1} # Just for demo...
p = aa(**data)
p.full_clean()
p.save()
Here is the problem, is the case that when I set the default value for a field, the blank is automatically set to True?
P.S. I know what are the differences between null and blank, I just want to figure out the issues related to the default.
If you put default on a model field, actually it is not setting blank = True . When you initialize a Model like
instance = ModelClass(**field_data)
django will check for all fields and if the field value is not there, then it will try checkin default value by calling get_default() method (look for default value) on Field class.
Django model fields default to required unless you specify blank=True and/or null=True. In your example, the model validates because you have defaulted a to 1 and set b to 1.1. Neither field is blank, so you're not encountering a situation that hits the question you're asking.
Try changing data = {'b': 1.1} to data = {'a': 1.1} and see what happens - you should get a field validation error.
Yes. Django always do that. If you don't need that feature simple make it set blank = False

SQLFORM.factory dropdownlist not mandatory

I have a custom form which I have designed using SQLForm.factory. I have created a dropdownlist in it as given below:
Field('method',requires=IS_IN_SET([(1,'Method1'),(2,'Method2'),(3,'Method3')],zero='Select'))
When I do not select any options from it it shows error as 'value not allowed'. I do not want it to be a mandatory field. I tried to set required argument of Field class's init method to False but it did not work. Please let me know how do I achieve that?
requires=IS_EMPTY_OR(IS_IN_SET(
[(1,'Method1'), (2,'Method2'), (3,'Method3')], zero='Select'))
Note, the required argument to Field() is enforced by the DAL upon inserts and results in an exception being thrown upon failure. The requires attribute includes a list of one or more validators that are processed during a SQLFORM submission -- violations of validators result in errors being displayed on the form, not Python exceptions.

Cannot assign "(<Blog: title1>, False)": "Post.blog" must be a "Blog" instance [duplicate]

I'm trying to use get_or_create for some fields in my forms, but I'm getting a 500 error when I try to do so.
One of the lines looks like this:
customer.source = Source.objects.get_or_create(name="Website")
The error I get for the above code is:
Cannot assign "(<Source: Website>, False)": "Customer.source"
must be a "Source" instance.
From the documentation get_or_create:
# get_or_create() a person with similar first names.
p, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
# get_or_create() didn't have to create an object.
>>> created
False
Explanation:
Fields to be evaluated for similarity, have to be mentioned outside defaults. Rest of the fields have to be included in defaults. In case CREATE event occurs, all the fields are taken into consideration.
It looks like you need to be returning into a tuple, instead of a single variable, do like this:
customer.source,created = Source.objects.get_or_create(name="Website")
get_or_create returns a tuple.
customer.source, created = Source.objects.get_or_create(name="Website")
get_or_create() returns a tuple:
customer.source, created = Source.objects.get_or_create(name="Website")
created → has a boolean value, is created or not.
customer.source → has an object of get_or_create() method.
Following #Tobu answer and #mipadi comment, in a more pythonic way, if not interested in the created flag, I would use:
customer.source, _ = Source.objects.get_or_create(name="Website")
The issue you are encountering is a documented feature of get_or_create.
When using keyword arguments other than "defaults" the return value of get_or_create is an instance. That's why it is showing you the parens in the return value.
you could use customer.source = Source.objects.get_or_create(name="Website")[0] to get the correct value.
Here is a link for the documentation:
http://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create-kwargs
get_or_create method would actually return a tuple.
The trick with the get_or_create method is that it actually returns a tuple of (object, created). The first element is an instance of the model you are trying to retrieve and the second is a boolean flag to tell if the instance was created or not. True means the instance was created by the get_or_create method and False means it was retrieved from the database
So you can do something like to get the source instance
``` customer.source = Source.objects.get_or_create(name="Website")[0]
```
Important warning.
you should take care of the following before using the get_or_create , https://docs.djangoproject.com/en/4.0/ref/models/querysets/.
....
Warning
This method is atomic assuming that the database enforces uniqueness of the keyword arguments (see unique or unique_together). If the fields used in the keyword arguments do not have a uniqueness constraint, concurrent calls to this method may result in multiple rows with the same parameters being inserted.

Correct way to use get_or_create?

I'm trying to use get_or_create for some fields in my forms, but I'm getting a 500 error when I try to do so.
One of the lines looks like this:
customer.source = Source.objects.get_or_create(name="Website")
The error I get for the above code is:
Cannot assign "(<Source: Website>, False)": "Customer.source"
must be a "Source" instance.
From the documentation get_or_create:
# get_or_create() a person with similar first names.
p, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
# get_or_create() didn't have to create an object.
>>> created
False
Explanation:
Fields to be evaluated for similarity, have to be mentioned outside defaults. Rest of the fields have to be included in defaults. In case CREATE event occurs, all the fields are taken into consideration.
It looks like you need to be returning into a tuple, instead of a single variable, do like this:
customer.source,created = Source.objects.get_or_create(name="Website")
get_or_create returns a tuple.
customer.source, created = Source.objects.get_or_create(name="Website")
get_or_create() returns a tuple:
customer.source, created = Source.objects.get_or_create(name="Website")
created → has a boolean value, is created or not.
customer.source → has an object of get_or_create() method.
Following #Tobu answer and #mipadi comment, in a more pythonic way, if not interested in the created flag, I would use:
customer.source, _ = Source.objects.get_or_create(name="Website")
The issue you are encountering is a documented feature of get_or_create.
When using keyword arguments other than "defaults" the return value of get_or_create is an instance. That's why it is showing you the parens in the return value.
you could use customer.source = Source.objects.get_or_create(name="Website")[0] to get the correct value.
Here is a link for the documentation:
http://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create-kwargs
get_or_create method would actually return a tuple.
The trick with the get_or_create method is that it actually returns a tuple of (object, created). The first element is an instance of the model you are trying to retrieve and the second is a boolean flag to tell if the instance was created or not. True means the instance was created by the get_or_create method and False means it was retrieved from the database
So you can do something like to get the source instance
``` customer.source = Source.objects.get_or_create(name="Website")[0]
```
Important warning.
you should take care of the following before using the get_or_create , https://docs.djangoproject.com/en/4.0/ref/models/querysets/.
....
Warning
This method is atomic assuming that the database enforces uniqueness of the keyword arguments (see unique or unique_together). If the fields used in the keyword arguments do not have a uniqueness constraint, concurrent calls to this method may result in multiple rows with the same parameters being inserted.

Categories