105 votes

CharField avec une longueur fixe, comment faire ?

Je voudrais avoir dans mon modèle un CharField avec une longueur fixe. En d'autres termes, je veux qu'une seule et unique longueur soit valide.

J'ai essayé de faire quelque chose comme

volumenumber = models.CharField('Numéro de volume', max_length=4, min_length=4)

mais cela me donne une erreur (il semble que je ne peux pas utiliser à la fois max_length et min_length en même temps).

Y a-t-il un autre moyen rapide ?

Mon modèle est le suivant :

class Volume(models.Model):
    vid = models.AutoField(primary_key=True)
    jid = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name="Journal")
    volumenumber = models.CharField('Numéro de volume')
    date_publication = models.CharField('Date de publication', max_length=6, blank=True)
    class Meta:
        db_table = u'volume'
        verbose_name = "Volume"
        ordering = ['jid', 'volumenumber']
        unique_together = ('jid', 'volumenumber')
    def __unicode__(self):
        return (str(self.jid) + ' - ' + str(self.volumenumber))

Ce que je veux, c'est que le numéro de volume doit être exactement de 4 caractères.

Par exemple, si quelqu'un insère '4b', Django renvoie une erreur car il attend une chaîne de 4 caractères.

J'ai donc essayé avec

volumenumber = models.CharField('Numéro de volume', max_length=4, min_length=4)

mais cela me donne cette erreur :

Validation des modèles...
Exception non gérée dans un thread démarré par 
Traceback (étapes de pile les plus récentes):
  File "/Library/Python/2.5/site-packages/django/core/management/commands/runserver.py", ligne 48, dans inner_run
    self.validate(display_num_errors=True)
  File "/Library/Python/2.5/site-packages/django/core/management/base.py", ligne 249, dans validate
    num_errors = get_validation_errors(s, app)
  File "/Library/Python/2.5/site-packages/django/core/management/validation.py", ligne 28, dans get_validation_errors
    for (app_name, error) in get_app_errors().items():
  File "/Library/Python/2.5/site-packages/django/db/models/loading.py", ligne 131, dans get_app_errors
    self._populate()
  File "/Library/Python/2.5/site-packages/django/db/models/loading.py", ligne 58, dans _populate
    self.load_app(app_name, True)
  File "/Library/Python/2.5/site-packages/django/db/models/loading.py", ligne 74, dans load_app
    models = import_module('.models', app_name)
  File "/Library/Python/2.5/site-packages/django/utils/importlib.py", ligne 35, dans import_module
    __import__(name)
  File "/Users/Giovanni/src/djangoTestSite/../djangoTestSite/journaldb/models.py", ligne 120, dans 
    class Volume(models.Model):
  File "/Users/Giovanni/src/djangoTestSite/../djangoTestSite/journaldb/models.py", ligne 123, dans Volume
    volumenumber = models.CharField('Volume Number', max_length=4, min_length=4)
TypeError: __init__() a reçu un argument de mot-clé inattendu 'min_length'

Cela n'apparaît évidemment pas si j'utilise uniquement "max_length" OU "min_length".

J'ai lu la documentation sur le site web de Django et il semble que j'ai raison (je ne peux pas les utiliser ensemble), c'est pourquoi je demande s'il y a une autre façon de résoudre le problème.

124voto

Chetan Points 759

Un peu dans la même lignée que ci-dessus, mais pour ce que cela vaut, vous pourriez également utiliser MinLengthValidator fourni par Django. Cela a fonctionné pour moi. Le code ressemblerait à ceci :

from django.core.validators import MinLengthValidator
...
class Volume(models.Model):
volumenumber = models.CharField('Numéro de volume', max_length=4, validators=[MinLengthValidator(4)])
...

102voto

tBuLi Points 208

Vous n'avez même pas à écrire un personnalisé. Utilisez simplement le RegexValidator que fournit Django.

from django.core.validators import RegexValidator

class MyModel(models.Model):
    myfield = models.CharField(validators=[RegexValidator(regex='^.{4}$', message='La longueur doit être de 4', code='nomatch')])

Depuis la documentation de Django : class RegexValidator(\[regex=None, message=None, code=None\])

regex: Une expression régulière valide à faire correspondre. Pour en savoir plus sur les expressions régulières en Python, consultez ce excellent HowTo : http://docs.python.org/howto/regex.html

message: Le message renvoyé à l'utilisateur en cas d'échec.

code: code d'erreur renvoyé par ValidationError. Non important pour votre cas d'utilisation, vous pouvez le laisser de côté.

Faites attention, l'expression régulière que je suggère permettra tout caractère, y compris les espaces. Pour n'autoriser que des caractères alphanumériques, remplacez le '.' par '\w' dans l'argument regex. Pour d'autres besoins, consultez la documentation ;).

50voto

Haes Points 6453

Les instances de champ de modèle de base de données CharField n'ont qu'un paramètre max_length, comme indiqué dans la documentation. C'est probablement parce qu'il n'y a qu'une contrainte de longueur de caractère maximale équivalente en SQL.

Les objets Champ de formulaire CharField, en revanche, ont un paramètre min_length. Vous devriez donc écrire un ModelForm personnalisé pour ce modèle spécifique et remplacer le formulaire de modèle d'administration par le formulaire personnalisé.

Quelque chose comme ça :

# admin.py

from django import forms

...

class VolumeForm(forms.ModelForm):
    volumenumber = forms.CharField(max_length=4, min_length=4)

    class Meta:
        model = Volume

class VolumeAdmin(admin.ModelAdmin):
    form = VolumeForm

...

admin.site.register(Volume, VolumeAdmin)

17voto

tjb Points 2869

Vous pouvez écrire un Validator personnalisé comme suggéré par @Ben. À la date de cette réponse, les instructions pour le faire se trouvent à https://docs.djangoproject.com/en/dev/ref/validators/

Le code ressemblerait à ceci (copié depuis le lien) :

from django.core.exceptions import ValidationError

def validate_length(value,length=6):
    if len(str(value))!=length:
        raise ValidationError(u'%s is not the correct length' % value)

from django.db import models

class MyModel(models.Model):
    constraint_length_charField = models.CharField(validators=[validate_length])

0voto

dtatarkin Points 121

Encore une autre implémentation utilisant champ de modèle personnalisé:

from django.core.validators import BaseValidator
from django.db import models
from django.utils.deconstruct import deconstructible

@deconstructible
class FixedLengthValidator(BaseValidator):
    message = 'Assurez-vous que cette valeur a %(limit_value)d caractère (elle en a %(show_value)d).'
    code = 'longueur'

    def compare(self, a, b):
        return a != b

    def clean(self, x):
        return len(x)

class FixedLengthCharField(models.CharField):
    def __init__(self, *args, length, **kwargs):
        self.length = length
        kwargs['max_length'] = length
        super().__init__(*args, **kwargs)
        self.validators.insert(0, FixedLengthValidator(length))

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        del kwargs['max_length']
        kwargs['length'] = self.length
        return name, path, args, kwargs

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X