Search code examples
pythondjangoclientdefault

Where do Django's Field's "default" and test's "client" parameters come from?


If you search for the parameters of a Field (e.g. IntergField) in Django through

from django.db import models 
dir(models.IntegerField)

you get 'default_error_messages', 'default_validators', 'unique', 'validate', 'validators', etc, but not "default" itself, although it's commonly used, as in

class Choice(models.Model):
    ...
    votes = models.IntegerField(default=0)

Same thing with "client". The docs say TestCase "comes with its own client". In this snippet from Django's docs, this client is explored

class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
        """
        If no questions exist, an appropriate message is displayed.
        """
        response = self.client.get(reverse('polls:index'))

but you can't find it through

from django.test import TestCase
dir(django.test.TestCase)

or even

dir(django.test.TestCase.client_class)

I'm asking where they come from, but also how to search for these "hidden" parameters, methods, etc.


Solution

  • The .default is not an attribute of the class, but of the instance. Indeed, if we look at the source code [GitHub], we see:

    class Field(RegisterLookupMixin):
        # …
    
        def __init__(self, verbose_name=None, name=None, primary_key=False,
                     max_length=None, unique=False, blank=False, null=False,
                     db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
                     serialize=True, unique_for_date=None, unique_for_month=None,
                     unique_for_year=None, choices=None, help_text='', db_column=None,
                     db_tablespace=None, auto_created=False, validators=(),
                     error_messages=None):
            # …
            self.default = default
            # …

    You thus can find this if you construct an IntegerField object:

    >>> from django.db.models import IntegerField
    >>> dir(IntegerField())
    ['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_backend_specific_checks', '_check_choices', '_check_db_index', '_check_deprecation_details', '_check_field_name', '_check_max_length_warning', '_check_null_allowed_for_primary_keys', '_check_validators', '_choices_is_value', '_clear_cached_lookups', '_db_tablespace', '_description', '_error_messages', '_get_default', '_get_flatchoices', '_get_lookup', '_unique', '_unregister_lookup', '_validators', '_verbose_name', 'auto_created', 'auto_creation_counter', 'blank', 'cached_col', 'cast_db_type', 'check', 'choices', 'class_lookups', 'clean', 'clone', 'contribute_to_class', 'creation_counter', 'db_check', 'db_column', 'db_index', 'db_parameters', 'db_returning', 'db_tablespace', 'db_type', 'db_type_parameters', 'db_type_suffix', 'deconstruct', 'default', 'default_error_messages', 'default_validators', 'description', 'descriptor_class', 'editable', 'empty_strings_allowed', 'empty_values', 'error_messages', 'flatchoices', 'formfield', 'get_attname', 'get_attname_column', 'get_choices', 'get_col', 'get_db_converters', 'get_db_prep_save', 'get_db_prep_value', 'get_default', 'get_filter_kwargs_for_object', 'get_internal_type', 'get_lookup', 'get_lookups', 'get_pk_value_on_save', 'get_prep_value', 'get_transform', 'has_default', 'help_text', 'hidden', 'is_relation', 'many_to_many', 'many_to_one', 'max_length', 'merge_dicts', 'name', 'null', 'one_to_many', 'one_to_one', 'pre_save', 'primary_key', 'register_lookup', 'rel_db_type', 'related_model', 'remote_field', 'run_validators', 'save_form_data', 'select_format', 'serialize', 'set_attributes_from_name', 'system_check_deprecated_details', 'system_check_removed_details', 'to_python', 'unique', 'unique_for_date', 'unique_for_month', 'unique_for_year', 'validate', 'validators', 'value_from_object', 'value_to_string', 'verbose_name']

    here the .default attribute is visible.

    For the TestCase, this is handled by the logic implemented in the SimpleTestCase. This will when it runs the testcase, set up the testcase and this includes populating the object with a .client that is an instance of the class refered to by the client_class class attribute

    class SimpleTestCase(unittest.TestCase):
        # …
        client_class = Client
    
        def __call__(self, result=None):
            """
            Wrapper around default __call__ method to perform common Django test
            set up. This means that user-defined Test Cases aren't required to
            include a call to super().setUp().
            """
            self._setup_and_call(result)
    
        def _setup_and_call(self, result, debug=False):
            # …
            self._pre_setup()
            # …
        
        def _pre_setup(self):
            # …
            self.client = self.client_class()
            # …
    
    class TransactionTestCase(SimpleTestCase):
        # …
    
    TestCase(TransactionTestCase):
        # …

    This is not uncommon. In Python very often attributes are defined on the object, or even defined later on by some method when running the testcase for example. You can only know this by reading the documentation.