Hello!*Making distinction between form validation and static validation.* I namedthe process of static checks of apps, models etc. "validation". Unfortunately,novices may confuse it with form validation. So I propose to rename it to"verification". Functions/methods/classmethods (referred to as just functions)starting with `validate_` will start with `verify_` now. And functionsdiscovering all other verification functions will be called `verify` insteadof `validate_all`. This is shorter than `validate_` (only 6 letters) and thereis no risk of confusing novices. I considered alternatives: "check" is toogeneral and "static_check" as well as "static_validate" are too long.
*Breaking points.* As Russell suggested me during private conversation, I willtry to merge my branch as often as possible. That should increase chance ofsuccess. There is one obvious breaking points: after finishing revampingvalidation.Merging while working on the framework shouldn't be too hard. That is becauseI will refactor bottom-up and keep all code and tests running and because Iwon't touch any public API. Actually, merging can be done after every week.First question: Django 1.6 is being released now and there is no 1.7branch yet; will be there any 1.7 branch where I can merge to?
*Some changes to public API.* On last Friday (7 June 2013) I gave a small talkabout my project at pykonik (a meeting of Python programmers in Kraków where Ilive). I was given some feedback.I changed fields of Error and Warning classes. Previously I proposed splittingerror message into `msg` (short error message), `explanation` (why is somesituation disallowed?) and `hint` (list of solutions). We agreed that`explanation` is redundant, so now I propose only two fields: `msg` and`hint`. `hint` will be optional, so you can set it to None, but you have to doit explicitly:Error("something is broken", hint=None)I hope that this will force developers to thing carefully if they cannot giveany useful hint and that it will improve quality of error messages.
*django-security* During the meeting, somebody referred to django-security [1],and I will have a look at it soon. Implementing some features of django-securitymay be a good idea if I finish merging django-secure earlier.
applabel.modelname: Two m2m relations through the same model.The model has two many-to-many relations through the intermediary Membershipmodel, which is not permitted.(No hint.)
Finally, I've rewritten all model and field tests and checks. Some new testswere written. First draft of checking framework documentation was written. Alltests passes (at least at my computer). I've rebased my branch(`gsoc2013-checks` [1]) against master. The branch is ready to deep review.
I've created pull request [2] for ticket [#19934]. The patch clarifies thatimporting `django.utils.image` cannot result in ImportError. I've also createdticket [#20735] about confusing ManyToManyField documentation. There is a pullrequest too [3]. Both pull requests were merged.
1. I wrote some tests [4] where nose tests generators [5] would be reallyhelpful. I tried to emulate them. Is the code I wrote a good approach?
2. Two m2m through the same model are forbidden. The new error message is:applabel.modelname: Two m2m relations through the same model.The model has two many-to-many relations through the intermediary Membershipmodel, which is not permitted.(No hint.)Is there any reasonable hint we can provide except for suggesting duplicatingthe intermediary model?
3. I've deleted `app_errors` [6] because it wasn't used anywhere (insideDjango). Was it left by accident or was there a reason for that?
4. I've renamed DatabaseValidation.validate_field to check_field and changed itsarguments. They were: `errors` -- ModelErrorCollection [7], `opts` -- meta ofthe model of the field and `f` -- the field; The method returned None. Now thereis only `field` argument and the method returns list of errors. It's not publicAPI, but we should go through deprecation process.How can we do that regarding changed arguments? We can call old `validate_field`method where the backend specific validation is triggered [8]. By default, theold method will call `check_field` method. The old one need to transform list oferrors into strings and add them to `errors` list. The problem is that at [8] weneed to convert the strings again to errors. That cannot be done in a gracefulway. Is there any other way to achieve deprecation?
Progress: I've implemented manager checks.The schedule for the next 4 weeks is:1. Manager checks (ref 4.1.7)(done)(0.5 week).2. Entry point registering (ref 4.1.5) & rewriting mechanism of triggeringchecking framework (ref 4.1.9)(1.5 week).3. Moving custom User model checks (ref 4.1.6)(0.5 week).4. Rewriting AdminModel checks and tests (ref 4.1.8)(1 week).5. Polishing doc (ref 4.1.10)(0.5 week).(ref 4.1.x are references to my original proposal [1])Let's talk about public API to register your own validation stuff. There willbe `register(callable)` function in `django.core.checks`. It will returnnothing. The callables will be stored in a global variable. They should obeythe same contract as `check` methods: they allow for `**kwargs` and returnlist of errors and warnings. All registered callables will be called duringchecking phase (i. e. when you type `manage.py validate` or before runningserver).
This API allows us to register, among other things, app-specific checks. Butit's not necessary to write an app in order to do some checks.`register` function will have two optional parameters: `run_in_production`(default: False) and `run_in_development` (default: True) that let you specifywhen the callable should be called. Most checks should be performed only indevelopment environment (which is equivalent to DEBUG==True). Security checksmakes sense only in production environment (<==> DEBUG==False).I would be happy if I could hear your opinion!
Progress: I've implemented registering entry points. Now there is`django.core.checks.register(callable)` function. There is no`run_in_development` and `run_in_production` arguments. I've also rewrittenmechanism of triggering checking framework -- `BaseCommand.validate` calls`django.core.checks.run_checks` instead of `get_validation_errors` (which wasdeleted) now.Questions:1. BaseCommand.validate have an `app` optional argument to run validation of aparticular app (if app==None then all checks are performed). Unfortunately, withthe current checking framework, we are not able to run checks for a particularapp. The attribute isn't used anywhere in Django except for three tests [1] andI don't think that this is heavily used by third party commands. So I propose todeprecate this attribute if it's possible.
2a. Warnings are printed with bold, yellow foreground now (errors use redcolor). Is it a good choice?
2b. The rules of formatting error messages are that the error message (that maybe multiline) is followed by the hint in the next line, i. e.:app_label.model_label: Error message.Error message can be multilined. In that case, the first line should be a short description and the rest should be a long description.HINT: Hints are printed in a new line.If hint is missing then there is no last line. If the invalid object is a fieldor a manager, then the error message starts with`app_label.model_label.field_or_manager_name: `. If it's neither a model, amanager nor a field, then '?: ' is printed. Do you have any opinion about thisstyle?
--
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 django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
For more options, visit https://groups.google.com/groups/opt_out.
I'm still working at polishing after reviewing. I've deprecated`requires_model_validation` and `validate`. I've started at adding tests forcontenttype fields: `GenericForeignKey` and `GenericRelation`.I've updated gsoc2013-checks-review branch [1]. Now it's the same asgsoc2013-checks branch [2]. I will push new work to the latter while the formerwill remain unchanged. I'm working at contenttype tests ingsoc2013-checks-contenttypes [3] branch. The work is not finished yet and thereare some failing tests.@Shai Berger: Thank you for creating the ticket. I'm sorry that I procrastinated accepting it -- I finally did it and proposed a patch. [4]Questions:1. Output formatting. We decided that every error/warning will take one line plusadditional one for a hint if it's provided. The justification is that a Djangouser may type "grep HINT" to filter all hints. But now I think it's unpracticalsince the lines with hints doesn't say which object is invalid. So we can: (1)put hint in the same line as the error message or (2) change the format to sthlike this:applabel.modellabel: Error message.applabel.modellabel: HINT: Hint.
2. Is it allowed to use `GenericRelation` pointing to a model if the model lacks`GenericForeignKey`?
3. I've added unicode_literals import to django/core/management.py but thisaffected `BaseCommand.verbosity` default value. In order not to break commands,I left the attribute as a bytestring [5]. Changing it to an unicode breaks some admin_scipts tests, i. e. `CommandTypes.test_app_command` expects in stdout:... options=[('pythonpath', None), ... ('verbosity', '1')]while the command prints:... options=[('pythonpath', None), ... ('verbosity', u'1')]
Progress.- Deprecated `requires_model_validation` flag and `validate` method (both`BaseCommand` members) in favour of new `requires_system_checks` flag and`check` method.- Finished working at `contenttypes` tests.- Improved code thanks to Preston Holmes comments. Deleted dead code and addedsome new tests to improve code coverage.It'd be nice to have checks of clashes between GenericForeignKey andaccessors. I didn't implemented it because little time left and I need tohurry up.
When it was easy, I've added new tests to improve code coverage. However, thereare still some tests that would be nice to have:- Test for raising an error while using an unique varchars longer than 255characters under MySQL backend. [1]- Test for `ImageField`. When `Pillow` is not installed and `ImageField` is inuse, an error should be raised. This should be tested. [2]- Test for raising warning/ImproperlyConfigured in `BaseCommand.__init__`. [3]
Filtering and silencing errors/warnings. We need two features: abilityto run only a subset of checks (aka filtering errors) and ability to silencesome errors.Silencing is easy. Every warning will have a unique name like "W027". The formatof the name is letter "W" followed by a unique number. The system checkframework is open ended and third-party apps can register its own checks. Forwarnings raised by these apps, "Wnumber.applabel" (e.g. "W001.myapp") style willbe used. Of course, everything can be changed and I'm open to yours suggestions,so feel free to comment and criticize it.There will be a new setting called `SILENCED_WARNINGS`. If you want to silence awarning, you put its name in this setting, e.g.:SILENCED_ERRORS = ['W027',]
Only light messages (warnings, infos and debugs) can be silenced.Running only a subset of check is a more complex task. We can associate everycheck with a set of tags (that can be done while registering checks). To runonly checks tagged "mytag" or "mysecondtag", you need to type:manage.py check mytag mysecondtagHowever, we would like to run checks of an app (or a set of apps):manage.py check auth adminThis is hard, because checks are no longer app-specific. I propose to solve thisby passing an `apps` optional keyword argument to every check function. Thefunction should only validate specified apps (or all apps if apps==None). Theonly problem is how to determine if we deal with a tag or app name? Considerthis command:manage.py check auth mytagThis should run all checks tagged "mytag". Only messages for `auth` app shouldbe reported. I propose to collect all tags while registering check functions andif a string is one of them, then interpret it as a tag, otherwise assume it's anapp name (and check if an app with given name exists).
Problems/questions.1. We decided to mimic message and logging framework, so every error is taggedwith a level. Its value is an integer indicating how important and serious isa message. There are five predefined values: CRITICAL, ERROR, WARNING, INFO,DEBUG. However, Preston Holmes noticed that this is unpractical because weneed only errors and warnings. I think we should discuss again if it's worthmimicing these frameworks.
2. On the pull request we started discussing names. "System check" is better than"check" but it suggest that it's connected with hardware. Preston proposed"Startup checks". I don't have strong opinion. To be honest, I don't thing"startup checks" is much more better than "system checks" so I will leave it asit is, but I'm still open to suggestions and I would like to see yours opinion.
4. In my last post I mentioned that there is a problem with`BaseCommand.verbosity`. I tried to convert its default value ('1') into anunicode, but I failed. This test command [5] prints its arguments to stdout,so the output is something like this:... options=[('pythonpath', None), ... ('verbosity', '1')]But when you change `verbosity` from a bytestring to an unicode then the outputis (only under Python 2):... options=[('pythonpath', None), ... ('verbosity', u'1')]Russell proposed to replace `sorted(options.items())` in [5] with:sorted((text_type(k), text_type(v)) for k, v in options.items())This is a solution, but it makes tests less strict. There is no way todistinguish between `None` value and "None" string -- both are printed in thesame way.I spent some time to find a better approach but with no success. The leastinsane way is to check if the object to print is an unicode and if it is,print it in the Python 3 style (without starting "u"); otherwise print itnormally.
Note that this is a more general problem. I've hit the same problem when I triedto write a test for `CheckMessage.__repr__` method. I wrote:def test_repr(self):e = Error("Error", hint="Hint", obj=None)expected = "<CheckMessage: level=40, msg='Error', hint='Hint', obj=None>"self.assertEqual(repr(e), expected)But under Python 2, `expected` variable should be:expected = "<CheckMessage: level=40, msg=u'Error', hint=u'Hint', obj=None>"
5. I've deleted one filter in [6]. I think it's useless. If`self.related.get_accessor_name` is None, then `self.rel.is_hidden()` is true,so this is a dead if. Am I right?
6. When `ForeignKey.foreign_related_fields` is a list of more than one element? Ifit's always a one-element list, then we don't need lines 1443-1457 in`ForeignKey._check_unique_target`. [7]
7. It looks like the code I've deleted in this commit [8] was dead, because if yourefer to an inherited field, an FieldDoesNotExist exception is raised by`get_field` method. Is that right?
8. I've added a new check. If you're using a `GenericRelation` but there is no`GenericForeignKey` on the target model, an warning is raised. This check wasimplemented in this commit [9]. It uses `vars` builtin function to check if thetarget model has a `GenericForeignKey`. This is ugly, but I don't see a betterapproach.
Schedule. There is little time to the end of GSoC -- only 5 weeks and I willwork 50% of full speed after September 6, because I'm going on holiday.- Rewriting admin checks (1 week).- Implementing filtering and silencing errors (1 week).- Merging django-secure (2 weeks?!).- Polishing doc, last review (1 week).I will implement filtering and silencing errors before merging django-securebecause these are a must if we want to merge django-secure.
8. I've added a new check. If you're using a `GenericRelation` but there is no`GenericForeignKey` on the target model, an warning is raised. This check wasimplemented in this commit [9]. It uses `vars` builtin function to check if thetarget model has a `GenericForeignKey`. This is ugly, but I don't see a betterapproach.Hrm. I don't really like this, but I'm not sure I have a better option. A better approach would be to have GFKs turn up in get_fields, but it isn't your responsibility to fix the internal problems of Generic Foreign Keys. If we have to live with this, then we should at the very least document it as a FIXME, pointing at the underlying problem with _meta handling of GFKs.
1. One of my questions left unanswered on the pull request [1] (I mean this oneabout documentation and `__str__` use.).
2. I've finished rewriting admin checks. I've renamed `admin_validation` to`admin_checks`. I would like you to have a deep look at `fk_name` and`exclude` checks [2] as well as `inline_formsets` tests [3] (especially errormessages).
2a. "applabel.modellabel[.fieldname]" is the format of error messages for modelsand fields. How should the one for admin look like? I propose"applabel.ModelAdminClassName".
2b. BaseModelAdmin, ModelAdmin and InlineModelAdmin classes and their checkslive in separated files. `options.py` defines these classes, but checks livesin `checks.py`. We want to have these two issues separated, otherwise classdefinitions would become too long. Python does not support open classes, so wecannot just add some `_check_*` methods in `checks.py` to existing classesdefined in `options.py`.The current approach is that `check` method is defined in `options.py`, but itdelegates to appropriate functions in `checks.py` [4]. Yes, I use functions --there is no need to have validator class, because we don't need to storeanything in its instances. However, the code is really ugly. I wonder if thereis any better approach.
1. Progress: I've made improvements to admin checks. I've also finishedimplementing filtering and silencing checks. I've rebased my branch againstmaster again.
2. I'm afraid that there is too little time to merge django-secure. September 16 issuggested 'pencils down' date, so there are only less than two weeks (12 days) +one buffer week, because firm 'pencils down' date is September 23. Mergingdjango-secure in such little time is actually impossible -- we must through outthis task from schedule. I really apologize for that.
My plan is that this week I will work at documentation (at this moment there isonly a draft). I will also try to implement as much "nice to have" things aspossible. These are:
- Writing tests which would be nice to have. I mean three tests:
- [#20974],
- test for raising error when ImageField is in use but Pillow is not installed,
- test for raising error in `BaseCommand.__init__`.
- Moving out checks performed in `__init__`.
- Checking clashes between accessors and GenericForeignKeys.
- Checks for grouped `choices`.
The second week and the backup week is for deep review. Regarding the amountof comments I got during the first review, I guess I need *at least* one weekfor deep review.
3. As I said, I've implemented both filtering and silencing system checks.You can run only a subset of checks like that:./manage.py check auth -t tag1 --tag tag2This command will run only these checks which are labeled with `tag1` or `tag2`tags and only `auth` app will be validated. If there is no app name, all appsare checked. If there is no tag name, all checks are performed.Your check function can be labeled in this way:from django.core.checks import register, tag@tag('mytag')def my_check_function(apps, **kwargs):# apps is a list of apps that should be validated; if None, all apps# should be checked.return []register(my_check_function)The `tag` decorator works only for entry-point functions, e.g. these one passedto `register` function. It doesn't work for checksfunctions/methods/classmethods which are called by entry-point functions(directly or indirectly).
To silence a specific error/warning, you need to append its `id` toSILENCED_SYSTEM_CHECKS setting. The `id` could be any string, but we use thefollowing convension: "E001" for core errors, "W002" for core warnings,"applabel.E001" for errors raised by an custom app (including contrib apps, i.e."sites.E001"). The error/warning number is unique to an app, e.g. "sites.E001","admin.E001" and "E001" are all allowed, but "E001" and "W001" are not OK. Youshould use "E001" and "W002". To create an error/warning with given id, pass itas a keyword argument:Error('Message', hint=None, obj=invalid_object, id='myapp.E001')
4. More important changes in code:- Introduced RAW_SETTINGS_MODULE [1]. I use it in compatibility checks to testif `TEST_RUNNER` setting was overriden [2].
- Introduced `BaseAppCache.get_models_from_apps` method [3]. This method returnsall models of given set of apps (or of all apps if None is passed) and is usedin `default_checks.py` [4].
- Admin changes are backward compatible. Admin validators are deprecated infavour of admin checks, i.e. `BaseValidator` is deprecated in favour of`BaseModelAdminChecks`. `BaseValidator` is an almost empty class now.`ModelAdmin.validator` class attribute is deprecated in favour of new `checks`attribute. If an ModelAdmin defines `validator`, we are not ignoring it.[2] https://github.com/chrismedrela/django/blob/gsoc2013-checks/django/core/checks/default_checks.py#L33[4] https://github.com/chrismedrela/django/blob/gsoc2013-checks/django/core/checks/default_checks.py
5. I've left one comment on the pull request. (I mean this one about movingregistering admin checks to `autodiscover`.)