I'm working on the importing script that saves data from CSV to Django database. Saving process looks like this:
instance = ModelName(**kwargs)
instance.save()
# No errors reported
But when I try to edit and save some items using admin panel it shows me a message that some of the field values (like URL fields and custom validators) is not valid.
Question: Is there any way to validate model instance from the python code using Django admin validators?
The issue is save() does not validate by default.
To address this, you can call the model's full_clean method to validate before calling save.
So, under the hood,
This method calls Model.clean_fields(), Model.clean(), and
Model.validate_unique() (if validate_unique is True), in that order
and raises a ValidationError that has a message_dict attribute
containing errors from all three stages.
The code would look something like this:
instance = ModelName(**kwargs)
instance.full_clean() #Does validation here
instance.save()
Related
I have a Django model that needs to call an external API just before saving. The API call is irreversible so I want to call it only before the DB save happens.
My code looks something like this:
class Model1(models.Model):
some_string = models.CharField(max_length=100)
class Model2(models.Model):
model1 = models.OneToOneField(Model1, on_delete=models.CASCADE)
num1 = models.IntegerField()
num2 = models.IntegerField()
api_output = models.models.JSONField()
def save(self, *args, **kwargs):
self.api_output = API.call_method(self.num1, self.num2, self.model1.some_string)
super().save(*args, **kwargs)
Model2 objects can be created from Django Admin but also from the code using Model2.objects.create().
I have 2 questions:
When creating an instance of Model2 from Django Admin - if the API call fails (throws an exception) I'd like Django Admin to show a human-readable error instead of the 5xx error page. One way to do that is to have a clean() method but I don't want to call the API before the object is actually saved. Also, when using Model2.objects.create() the clean() method is not called. Is there a way to call the API inside save() and still have Django Admin print a nice error if the call fails?
I noticed that during save(), if the OneToOneField constraint is violated (for example trying to create 2 instances of Model2 with the same instance of Model1), the validation happens after the API call which means the API is being called although the object is not valid. Is there a way to perform all validations before my code runs (which call the API)?
You should use Django Signals for that kind of business logic, if you want to do something before saving a model check the pre_save signal.
If you want to use Django Admin, you have to work in your admin.py inside your app and override the save_model method of ModelAdmin.
I am using Django 1.11, in one of my models I have added actions when the model is saved.
However I don't want these actions to be done when only a part of the model is saved.
I know that update_fields=('some_field',) can be used to specify which field must be saved.
But, when the object has been fetched in the database using the methods only() or defer() I don't see any information about the fields updated in the save() method, update_fields is empty.
Thus my question: How can I get the fields saved by Django when only some fields have been fetched ?
When you use defer or only to load an instance, the get_deferred_fields() method returns a list of field names that have not been loaded; you should be able to use this to work out which ones will be saved.
I am using Django's forms to validate API PATCH requests. In the "view" (which I use in quotes because it isn't really a view directly, it is a restless Resource, but that should be irrelevant here) which handles this patch request, self.data contains a dictionary of changes to some of the fields of the License object. I want to instantiate a ModelForm with the instance of the object to be changed. Clearly, though, I am misunderstanding how this works. See below:
def handle_patch(self, pk):
license = License.objects.get(id=pk)
form = LicenseResourceForm(self.data, instance=license)
if not form.is_valid():
print(form.errors)
If I pass a few fields as data to the above function, form.errors complains about every other required field of the License model, meaning I'm clearly not understanding how setting an instance on a ModelForm works.
I added a few debug prints to Django's ModelForm code itself in the clean() method, and as it begins to do the cleaning process, I can see that self.instance is populated with the instance of License that I expect, which confuses me - the ModelForm object knows the instance, but isn't using it to "fill in the blanks" so to speak.
So what am I misunderstanding? I must be doing this wrong.
EDIT I realize that some of you may want to see the LicenseResourceForm itself, so here it is, including my debug print:
class LicenseResourceForm(ModelForm):
"""Form for License Resource create and change endpoints."""
class Meta(object):
model = License
fields = ['customer', 'service', 'enabled', 'not_valid_before', 'not_valid_after']
def clean(self):
try:
print(self.instance)
super().clean()
except Exception as e:
print(e)
Django forms aren't meant for API use and don't understand PATCH semantics. They are meant for the workflow of a user entering or changing data in a web form, which will always post all the data to the backend. Therefore, all fields listed in the fields attribute of the form will be checked against the data, and any missing fields will be validated as blank.
You could probably fix this by doing something clever to dynamically set the list of fields based on the data supplied, but really you should use the appropriate tool for validating your data. I don't know restless, but django-rest-framework has serializers which can be used for this.
I am trying to implement an SSO login, deriving all the authorization rights from saml response:
class SAMLServiceProviderBackend(object):
def authenticate(self, saml_authentication=None):
if not saml_authentication: # Using another authentication method
return None
if saml_authentication.is_authenticated():
attributes = saml_authentication.get_attributes()
user = User(username=saml_authentication.get_nameid())
user.first_name = attributes['givenName']
user.last_name = attributes['sn']
return None
in views I got something like
...
user = authenticate(saml_authentication=auth)
login(self.request, user)
...
login fails because of the missing save() method. The only way would be to inherit from User and override the save method. Trying this, I got the next errors with is_authenticated, get_and_delete_messages, and so on
Is there an easy way to insert a user object into session, without saving the user to database?
Something like:
request.session['user'] = authenticate(saml_authentication=auth)
I guess should be possible with some limitations, eg. you cannot save your data with a user being a FK.
I have tried this myself, I suspect that you can dynamically create a user instance in the authenticate() method just don't call user.save(), or overwrite the save() method to do nothing.
You might also need to hook up the user record between requests, so you might need to write your own serializer for the user and load that construct the user instance back from session.
I'm fairly certain that Django's session objects are required for the authentication backend. i.e. if you login, then Django needs to store a reference to this fact somewhere. Django generally uses it's database to do this, however, if you're not using a database with your app then you can look at caching sessions instead:
https://docs.djangoproject.com/en/1.11/topics/http/sessions/#using-cached-sessions
(I'm assuming you're not using a database judging by your question)
More importantly however, depending on your needs, you may need to look at creating / configuring, a custom User Model in order to get your backend to work:
https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#substituting-a-custom-user-model
I am working on a django project and creating many model instances from batch upload form. I am creating many unsaved model instances in order to test them for errors as I don't want to enter any instances until the user has submitted a full set of valid records to avoid unintentional duplications within the database. My question is whether there is a good reason to use either save(commit=False) or full_clean on the unsaved model instances. I am currently using full_clean, but not sure what the differences/benefits are of one versus the other.
Calling full_clean() is the correct way to validate a model instance.
from django.core.exceptions import ValidationError
try:
obj.full_clean()
except ValidationError:
# handle invalid object
When dealing with a model form, calling is_valid() will perform the model validation, so you don't have to call full_clean() manually.
Calling save() with commit=False doesn't perform model validation. Instead, it gives you the opportunity to change the object before you save it to the database. A common example is to set the user attribute to the user that is currently logged on.