Hello! This summer I'm a student of Google Summer of Code. I was working at introducing a new system check framework (merging django-secure was a part of my proposal, but I didn't manage to do it due to my time overestimation). The project is coming to the end and there is a pull request [1] that is almost ready for merging. I would like to present you with the capabilities of the framework as well as a high-level overview. I would like to hear your opinion how the framework can be improved -- this is a last chance for that! This thread is a continuation of old "[GSoC] Revamping validation framework and merging django-secure once again" thread [2].
[1] https://github.com/django/django/pull/1364 [2] https://groups.google.com/forum/#!topic/django-developers/fEf21dtpqDE Here I would like to say thank you to my mentor, Russell Keith-Magee, who gave me a lot of advices, kept an eye at my project, did reviews and supported me. Preston Holmes also spent a lot of time reviewing code. Many thanks to numerous other contributors! Now let me introduce you the framework. *Overview* I've introduced a new system check framework that replaces the old validation framework performing mainly model validation. The new framework is responsible for model validation, app-specific checks like ModelAdmin validation as well as compatibility checks. It introduces concept of warnings. Warnings as well as errors can be silenced. The framework is open-ended which means that you can register your own check stuff. *Messages* The framework uses a concept of messages similar to messages from message framework or logging framework. A message can be a light message like a warning, info or debug; or a serious message, e.g. an error or critical (error). Every message consists of four attributes: - required error message (`msg`); - optional `hint`; in order to force developers to think carefully if they cannot provide any useful hint, they need to explicitly pass hint=None in that case; - optional invalid object (`obj`); - optional unique message identifier (`id`) used to refer to messages, i.e. "E001" (a core error) or "admin.W001" (a warning issued by admin app). *Performing checks* In order to perform all kinds of checks, type: python manage.py check This command does the same stuff as the old `validate` and `check` commands. `validate` is now deprecated and delegates to `check` command. You can still validate some specific apps: python manage.py check admin auth Or you can run a subset of checks labeled with a given tag: python manage.py -t compatibility # alternative: --tag compatibility *Silencing errors/warnings* A new setting called SILENCED_SYSTEM_CHECKS was introduced. It's a list of message identifiers. If you put on the list a light message like a warning, it won't be printed anymore. Putting on the list a serious message like an error won't hide the message, but will allow you to i.e. run server. *Registering your own stuff* If you want to add a new check, you need to write a function that accepts `apps` and `**kwargs` arguments where `apps` is a list of applications that should be validated (or None if all apps should be validated). The function should return a list of messages, even if it's an empty list. Finally, you need to register this entry point. Let's look at this snippet implementing some security checks: from django.conf import settings from django.core import checks # Label this entry point with 'security' tag. This tag can be used while # invoking check command, e.g. you can write `./manage.py -t security`. @checks.register('security') def my_security_check(apps, **kwargs): # This check does not perform any app-specific checks, so if we are # asked to validate some specific apps, we can skip checks. if apps is not None: return [] if len(settings.SECRET_KEY) < 64: return [ checks.Warning( 'Your SECRET_KEY is too short.', hint=None, # need to explicitly pass hint even if you cannot provide any hint obj='settings.SECRET_KEY', id='security.W001', ) ] else: return [] *Field/model/manager checks* You don't have to register your check stuff if it's called from another piece of code that is registered (directly or indirectly). This is true especially in the case of fields, models and managers. Each of these object have `check` method (or classmethod in the case of models) that can be overriden in order to add some validation stuff. Consider that you implemented a field that accepts only values from [min, max] range and you want to check if min < max. Here is a snippet of code: from django.core import checks from django.db import models class RangedIntegerField(models.IntegerField): def __init__(self, min=None, max=None, **kwargs): super(RangedIntegerField, self).__init__(**kwargs) self.min = min self.max = max def check(self, **kwargs): errors = super(RangedIntegerField, self).check(**kwargs) # (1) call the superclass # (2) Do some custom checks and add messages to `errors`: errors.extend(self._check_min_max_values(**kwargs)) return errors # (3) return all messages def _check_min_max_values(self, **kwargs): if (self.min is not None and self.max is not None and self.min > self.max): return [ checks.Error( 'min greated than max.', hint='Lower min or upper max.', obj=self, id='myapp.E001', ) ] return [] # When no error, return an empty list -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/django-developers. For more options, visit https://groups.google.com/groups/opt_out.
