Ticket #12385: 12385-class-attribute-for-i18n.diff

File 12385-class-attribute-for-i18n.diff, 19.6 KB (added by ramiro, 5 years ago)
  • django/contrib/admindocs/tests/__init__.py

    diff -r 783b8c4c26f8 django/contrib/admindocs/tests/__init__.py
    a b  
    11import unittest
     2import fields
    23from django.contrib.admindocs import views
    3 import fields
     4from django.db.models import fields as builtin_fields
    45
    5 from django.db.models import fields as builtin_fields
    66
    77class TestFieldType(unittest.TestCase):
    88    def setUp(self):
    99        pass
    10        
     10
    1111    def test_field_name(self):
    1212        self.assertRaises(AttributeError,
    1313            views.get_readable_field_data_type, "NotAField"
    1414        )
    15        
     15
    1616    def test_builtin_fields(self):
    1717        self.assertEqual(
    1818            views.get_readable_field_data_type(builtin_fields.BooleanField()),
    1919            u'Boolean (Either True or False)'
    2020        )
    21    
     21
    2222    def test_custom_fields(self):
    2323        self.assertEqual(
    2424            views.get_readable_field_data_type(fields.CustomField()),
    2525            u'A custom field type'
    2626        )
    2727        self.assertEqual(
    28             views.get_readable_field_data_type(fields.DocstringLackingField()),
    29             u'Field of type: DocstringLackingField'
     28            views.get_readable_field_data_type(fields.DescriptionLackingField()),
     29            u'Field of type: DescriptionLackingField'
    3030        )
    31    
    32     def test_multiline_custom_field_truncation(self):
    33         self.assertEqual(
    34             views.get_readable_field_data_type(fields.ManyLineDocstringField()),
    35             u'Many-line custom field'
    36         )
  • django/contrib/admindocs/tests/fields.py

    diff -r 783b8c4c26f8 django/contrib/admindocs/tests/fields.py
    a b  
    11from django.db import models
    22
    33class CustomField(models.Field):
    4     """A custom field type"""
    5    
    6 class ManyLineDocstringField(models.Field):
    7     """Many-line custom field
    8    
    9     This docstring has many lines.  Lorum ipsem etc. etc.  Four score
    10     and seven years ago, and so on and so forth."""
     4    description = "A custom field type"
    115
    12 class DocstringLackingField(models.Field):
     6class DescriptionLackingField(models.Field):
    137    pass
  • django/contrib/admindocs/views.py

    diff -r 783b8c4c26f8 django/contrib/admindocs/views.py
    a b  
    327327    return ''
    328328
    329329def get_readable_field_data_type(field):
    330     """Returns the first line of a doc string for a given field type, if it
    331     exists.  Fields' docstrings can contain format strings, which will be
    332     interpolated against the values of Field.__dict__ before being output. 
    333     If no docstring is given, a sensible value will be auto-generated from
    334     the field's class name."""
     330    """Returns the description for a given field type, if it exists,
     331    Fields' descriptions can contain format strings, which will be interpolated
     332    against the values of field.__dict__ before being output."""
    335333
    336     if field.__doc__:
    337         doc = field.__doc__.split('\n')[0]
    338         return _(doc) % field.__dict__
    339     else:
    340         return _(u'Field of type: %(field_type)s') % {
    341             'field_type': field.__class__.__name__
    342         }
     334    return field.description % field.__dict__
    343335
    344336def extract_views_from_urlpatterns(urlpatterns, base=''):
    345337    """
  • django/contrib/gis/db/models/fields/__init__.py

    diff -r 783b8c4c26f8 django/contrib/gis/db/models/fields/__init__.py
    a b  
     1from django.utils.translation import ugettext_lazy as _
    12from django.contrib.gis import forms
    23# Getting the SpatialBackend container and the geographic quoting method.
    34from django.contrib.gis.db.backend import SpatialBackend, gqn
     
    3031    return _srid_cache[srid]
    3132
    3233class GeometryField(SpatialBackend.Field):
    33     """The base GIS field -- maps to the OpenGIS Specification Geometry type."""
     34    "The base GIS field -- maps to the OpenGIS Specification Geometry type."
    3435
    3536    # The OpenGIS Geometry name.
    3637    geom_type = 'GEOMETRY'
     
    3839    # Geodetic units.
    3940    geodetic_units = ('Decimal Degree', 'degree')
    4041
     42    description = _("The base GIS field -- maps to the OpenGIS Specification Geometry type.")
     43
    4144    def __init__(self, verbose_name=None, srid=4326, spatial_index=True, dim=2, **kwargs):
    4245        """
    4346        The initialization function for geometry fields.  Takes the following
     
    257260
    258261# The OpenGIS Geometry Type Fields
    259262class PointField(GeometryField):
    260     """Point"""
    261263    geom_type = 'POINT'
     264    description = _("Point")
    262265
    263266class LineStringField(GeometryField):
    264     """Line string"""
    265267    geom_type = 'LINESTRING'
     268    description = _("Line string")
    266269
    267270class PolygonField(GeometryField):
    268     """Polygon"""
    269271    geom_type = 'POLYGON'
     272    description = _("Polygon")
    270273
    271274class MultiPointField(GeometryField):
    272     """Multi-point"""
    273275    geom_type = 'MULTIPOINT'
     276    description = _("Multi-point")
    274277
    275278class MultiLineStringField(GeometryField):
    276     """Multi-line string"""
    277279    geom_type = 'MULTILINESTRING'
     280    description = _("Multi-line string")
    278281
    279282class MultiPolygonField(GeometryField):
    280     """Multi polygon"""
    281283    geom_type = 'MULTIPOLYGON'
     284    description = _("Multi polygon")
    282285
    283286class GeometryCollectionField(GeometryField):
    284     """Geometry collection"""
    285287    geom_type = 'GEOMETRYCOLLECTION'
     288    description = _("Geometry collection")
  • django/contrib/localflavor/us/models.py

    diff -r 783b8c4c26f8 django/contrib/localflavor/us/models.py
    a b  
    11from django.conf import settings
     2from django.utils.translation import ugettext_lazy as _
    23from django.db.models.fields import Field, CharField
    34from django.contrib.localflavor.us.us_states import STATE_CHOICES
    4  
     5
    56class USStateField(CharField):
    6     """U.S. state (two uppercase letters)"""
     7
     8    description = _("U.S. state (two uppercase letters)")
     9
    710    def __init__(self, *args, **kwargs):
    811        kwargs['choices'] = STATE_CHOICES
    912        kwargs['max_length'] = 2
    1013        super(USStateField, self).__init__(*args, **kwargs)
    11  
     14
    1215class PhoneNumberField(Field):
    13     """Phone number"""
     16
     17    description = _("Phone number")
     18
    1419    def get_internal_type(self):
    1520        return "PhoneNumberField"
    1621
  • django/db/models/fields/__init__.py

    diff -r 783b8c4c26f8 django/db/models/fields/__init__.py
    a b  
    4949#     getattr(obj, opts.pk.attname)
    5050
    5151class Field(object):
    52     """Base class for all field types"""
    53    
    5452    # Designates whether empty strings fundamentally are allowed at the
    5553    # database level.
    5654    empty_strings_allowed = True
     
    6159    creation_counter = 0
    6260    auto_creation_counter = -1
    6361
     62    # Generic field type description, usually overriden by subclasses
     63    def _description(self):
     64        return _(u'Field of type: %(field_type)s') % {
     65            'field_type': self.__class__.__name__
     66        }
     67    description = property(_description)
     68
    6469    def __init__(self, verbose_name=None, name=None, primary_key=False,
    6570            max_length=None, unique=False, blank=False, null=False,
    6671            db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
     
    342347        return getattr(obj, self.attname)
    343348
    344349class AutoField(Field):
    345     """Integer"""
    346    
     350    description = ugettext_lazy("Integer")
    347351    empty_strings_allowed = False
    348 
    349352    def __init__(self, *args, **kwargs):
    350353        assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
    351354        kwargs['blank'] = True
     
    375378        return None
    376379
    377380class BooleanField(Field):
    378     """Boolean (Either True or False)"""
    379 
    380381    empty_strings_allowed = False
    381 
     382    description = ugettext_lazy("Boolean (Either True or False)")
    382383    def __init__(self, *args, **kwargs):
    383384        kwargs['blank'] = True
    384385        if 'default' not in kwargs and not kwargs.get('null'):
     
    421422        return super(BooleanField, self).formfield(**defaults)
    422423
    423424class CharField(Field):
    424     """String (up to %(max_length)s)"""
    425    
     425    description = ugettext_lazy("String (up to %(max_length)s)")
    426426    def get_internal_type(self):
    427427        return "CharField"
    428428
     
    444444
    445445# TODO: Maybe move this into contrib, because it's specialized.
    446446class CommaSeparatedIntegerField(CharField):
    447     """Comma-separated integers"""
    448    
     447    description = ugettext_lazy("Comma-separated integers")
    449448    def formfield(self, **kwargs):
    450449        defaults = {
    451450            'form_class': forms.RegexField,
     
    461460ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$')
    462461
    463462class DateField(Field):
    464     """Date (without time)"""
    465    
     463    description = ugettext_lazy("Date (without time)")
    466464    empty_strings_allowed = False
    467 
    468465    def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
    469466        self.auto_now, self.auto_now_add = auto_now, auto_now_add
    470467        #HACKs : auto_now_add/auto_now should be done as a default or a pre_save.
     
    539536        return super(DateField, self).formfield(**defaults)
    540537
    541538class DateTimeField(DateField):
    542     """Date (with time)"""
    543    
     539    description = ugettext_lazy("Date (with time)")
    544540    def get_internal_type(self):
    545541        return "DateTimeField"
    546542
     
    600596        return super(DateTimeField, self).formfield(**defaults)
    601597
    602598class DecimalField(Field):
    603     """Decimal number"""
    604    
    605599    empty_strings_allowed = False
    606 
     600    description = ugettext_lazy("Decimal number")
    607601    def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
    608602        self.max_digits, self.decimal_places = max_digits, decimal_places
    609603        Field.__init__(self, verbose_name, name, **kwargs)
     
    657651        return super(DecimalField, self).formfield(**defaults)
    658652
    659653class EmailField(CharField):
    660     """E-mail address"""
    661    
     654    description = ugettext_lazy("E-mail address")
    662655    def __init__(self, *args, **kwargs):
    663656        kwargs['max_length'] = kwargs.get('max_length', 75)
    664657        CharField.__init__(self, *args, **kwargs)
     
    669662        return super(EmailField, self).formfield(**defaults)
    670663
    671664class FilePathField(Field):
    672     """File path"""
    673    
     665    description = ugettext_lazy("File path")
    674666    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
    675667        self.path, self.match, self.recursive = path, match, recursive
    676668        kwargs['max_length'] = kwargs.get('max_length', 100)
     
    690682        return "FilePathField"
    691683
    692684class FloatField(Field):
    693     """Floating point number"""
    694    
    695685    empty_strings_allowed = False
     686    description = ugettext_lazy("Floating point number")
    696687
    697688    def get_db_prep_value(self, value):
    698689        if value is None:
     
    717708        return super(FloatField, self).formfield(**defaults)
    718709
    719710class IntegerField(Field):
    720     """Integer"""
    721    
    722711    empty_strings_allowed = False
    723 
     712    description = ugettext_lazy("Integer")
    724713    def get_db_prep_value(self, value):
    725714        if value is None:
    726715            return None
     
    744733        return super(IntegerField, self).formfield(**defaults)
    745734
    746735class IPAddressField(Field):
    747     """IP address"""
    748    
    749736    empty_strings_allowed = False
    750 
     737    description = ugettext_lazy("IP address")
    751738    def __init__(self, *args, **kwargs):
    752739        kwargs['max_length'] = 15
    753740        Field.__init__(self, *args, **kwargs)
     
    761748        return super(IPAddressField, self).formfield(**defaults)
    762749
    763750class NullBooleanField(Field):
    764     """Boolean (Either True, False or None)"""
    765 
    766751    empty_strings_allowed = False
    767 
     752    description = ugettext_lazy("Boolean (Either True, False or None)")
    768753    def __init__(self, *args, **kwargs):
    769754        kwargs['null'] = True
    770755        Field.__init__(self, *args, **kwargs)
     
    804789        return super(NullBooleanField, self).formfield(**defaults)
    805790
    806791class PositiveIntegerField(IntegerField):
    807     """Integer"""
    808    
     792    description = ugettext_lazy("Integer")
    809793    def get_internal_type(self):
    810794        return "PositiveIntegerField"
    811795
     
    815799        return super(PositiveIntegerField, self).formfield(**defaults)
    816800
    817801class PositiveSmallIntegerField(IntegerField):
    818     """Integer"""
    819 
     802    description = ugettext_lazy("Integer")
    820803    def get_internal_type(self):
    821804        return "PositiveSmallIntegerField"
    822805
     
    826809        return super(PositiveSmallIntegerField, self).formfield(**defaults)
    827810
    828811class SlugField(CharField):
    829     """String (up to %(max_length)s)"""
    830 
     812    description = ugettext_lazy("String (up to %(max_length)s)")
    831813    def __init__(self, *args, **kwargs):
    832814        kwargs['max_length'] = kwargs.get('max_length', 50)
    833815        # Set db_index=True unless it's been set manually.
     
    844826        return super(SlugField, self).formfield(**defaults)
    845827
    846828class SmallIntegerField(IntegerField):
    847     """Integer"""
    848    
     829    description = ugettext_lazy("Integer")
    849830    def get_internal_type(self):
    850831        return "SmallIntegerField"
    851832
    852833class TextField(Field):
    853     """Text"""
    854    
     834    description = ugettext_lazy("Text")
    855835    def get_internal_type(self):
    856836        return "TextField"
    857837
     
    861841        return super(TextField, self).formfield(**defaults)
    862842
    863843class TimeField(Field):
    864     """Time"""
    865    
     844    description = ugettext_lazy("Time")
    866845    empty_strings_allowed = False
    867 
    868846    def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
    869847        self.auto_now, self.auto_now_add = auto_now, auto_now_add
    870848        if auto_now or auto_now_add:
     
    936914        return super(TimeField, self).formfield(**defaults)
    937915
    938916class URLField(CharField):
    939     """URL"""
    940    
     917    description = ugettext_lazy("URL")
    941918    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
    942919        kwargs['max_length'] = kwargs.get('max_length', 200)
    943920        self.verify_exists = verify_exists
     
    949926        return super(URLField, self).formfield(**defaults)
    950927
    951928class XMLField(TextField):
    952     """XML text"""
    953    
     929    description = ugettext_lazy("XML text")
    954930    def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
    955931        self.schema_path = schema_path
    956932        Field.__init__(self, verbose_name, name, **kwargs)
  • django/db/models/fields/files.py

    diff -r 783b8c4c26f8 django/db/models/fields/files.py
    a b  
    209209        instance.__dict__[self.field.name] = value
    210210
    211211class FileField(Field):
    212     """File path"""
    213    
    214212    # The class to wrap instance attributes in. Accessing the file object off
    215213    # the instance will always return an instance of attr_class.
    216214    attr_class = FieldFile
     
    218216    # The descriptor to use for accessing the attribute off of the class.
    219217    descriptor_class = FileDescriptor
    220218
     219    description = ugettext_lazy("File path")
     220
    221221    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
    222222        for arg in ('primary_key', 'unique'):
    223223            if arg in kwargs:
     
    325325        super(ImageFieldFile, self).delete(save)
    326326
    327327class ImageField(FileField):
    328     """File path"""
    329    
    330328    attr_class = ImageFieldFile
    331329    descriptor_class = ImageFileDescriptor
     330    description = ugettext_lazy("File path")
    332331
    333332    def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
    334333        self.width_field, self.height_field = width_field, height_field
  • django/db/models/fields/related.py

    diff -r 783b8c4c26f8 django/db/models/fields/related.py
    a b  
    691691        return self.to._meta.pk
    692692
    693693class ForeignKey(RelatedField, Field):
    694     """Foreign Key (type determined by related field)"""
    695    
    696694    empty_strings_allowed = False
     695    description = ugettext_lazy("Foreign Key (type determined by related field)")
    697696    def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
    698697        try:
    699698            to_name = to._meta.object_name.lower()
     
    790789        return rel_field.db_type()
    791790
    792791class OneToOneField(ForeignKey):
    793     """One-to-one relationship
    794    
     792    """
    795793    A OneToOneField is essentially the same as a ForeignKey, with the exception
    796794    that always carries a "unique" constraint with it and the reverse relation
    797795    always returns the object pointed to (since there will only ever be one),
    798     rather than returning a list."""
    799 
     796    rather than returning a list.
     797    """
     798    description = ugettext_lazy("One-to-one relationship")
    800799    def __init__(self, to, to_field=None, **kwargs):
    801800        kwargs['unique'] = True
    802801        super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
     
    850849    })
    851850
    852851class ManyToManyField(RelatedField, Field):
    853     """Many-to-many relationship"""
    854    
     852    description = ugettext_lazy("Many-to-many relationship")
    855853    def __init__(self, to, **kwargs):
    856854        try:
    857855            assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)
  • docs/howto/custom-model-fields.txt

    diff -r 783b8c4c26f8 docs/howto/custom-model-fields.txt
    a b  
    55===========================
    66
    77.. versionadded:: 1.0
     8.. currentmodule:: django.db.models
    89
    910Introduction
    1011============
     
    165166    from django.db import models
    166167
    167168    class HandField(models.Field):
    168         """A hand of cards (bridge style)"""
     169
     170        description = "A hand of cards (bridge style)"
    169171
    170172        def __init__(self, *args, **kwargs):
    171173            kwargs['max_length'] = 104
     
    248250For example::
    249251
    250252    class HandField(models.Field):
    251         """A hand of cards (bridge style)"""
     253
     254        description = "A hand of cards (bridge style)"
    252255
    253256        __metaclass__ = models.SubfieldBase
    254257
     
    262265Documenting your Custom Field
    263266-----------------------------
    264267
     268.. class:: django.db.models.Field
     269
     270.. attribute:: description
     271
    265272As always, you should document your field type, so users will know what it is.
    266 The best way to do this is to simply provide a docstring for it.  This will
    267 automatically be picked up by ``django.contrib.admindocs``, if you have it
    268 installed, and the first line of it will show up as the field type in the
    269 documentation for any model that uses your field.  In the above examples, it
    270 will show up as 'A hand of cards (bridge style)'.  Note that if you provide a
    271 more verbose docstring, only the first line will show up in
    272 ``django.contrib.admindocs``.  The full docstring will, of course, still be
    273 available through ``pydoc`` or the interactive interpreter's ``help()``
    274 function.
     273In addition to providing a docstring for it, which is useful for developers,
     274you can also allow users of the admin app to see a short description of the
     275field type via the ``django.contrib.admindocs`` application if you have it
     276installed, in a way similar to how the fields builtin to Django` are show. For
     277this you need to provide such a descriptive text in a ``description`` class
     278attribute of your custom field. In the above examples, it will show up as
     279'A hand of cards (bridge style)'.
    275280
    276281Useful methods
    277282--------------
Back to Top