diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index 1f2a2db981..9172d938d2 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -73,7 +73,7 @@ class Command(BaseCommand): model_list = get_models(app) for model in model_list: - objects.extend(model.objects.all()) + objects.extend(model._default_manager.all()) try: return serializers.serialize(format, objects, indent=indent) diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index fe945baff7..3caa13e0bf 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -25,6 +25,10 @@ class BaseDatabaseCreation(object): def __init__(self, connection): self.connection = connection + def _digest(self, *args): + "Generate a 32 bit digest of a set of arguments that can be used to shorten identifying names" + return '%x' % (abs(hash(args)) % (1<<32)) + def sql_create_model(self, model, style, known_models=set()): """ Returns the SQL required to create a single model, as a tuple of: @@ -128,7 +132,7 @@ class BaseDatabaseCreation(object): col = opts.get_field(f.rel.field_name).column # For MySQL, r_name must be unique in the first 64 characters. # So we are careful with character usage here. - r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) + r_name = '%s_refs_%s_%s' % (r_col, col, self._digest(r_table, table)) final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ (qn(r_table), qn(truncate_name(r_name, self.connection.ops.max_name_length())), qn(r_col), qn(table), qn(col), @@ -187,8 +191,7 @@ class BaseDatabaseCreation(object): output.append('\n'.join(table_output)) for r_table, r_col, table, col in deferred: - r_name = '%s_refs_%s_%x' % (r_col, col, - abs(hash((r_table, table)))) + r_name = '%s_refs_%s_%s' % (r_col, col, self._digest(r_table, table)) output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % (qn(r_table), qn(truncate_name(r_name, self.connection.ops.max_name_length())), @@ -289,7 +292,7 @@ class BaseDatabaseCreation(object): col = f.column r_table = model._meta.db_table r_col = model._meta.get_field(f.rel.field_name).column - r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) + r_name = '%s_refs_%s_%s' % (col, r_col, self._digest(table, r_table)) output.append('%s %s %s %s;' % \ (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(table)), diff --git a/django/db/models/base.py b/django/db/models/base.py index 12e84625a7..c7596b37b7 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -411,32 +411,40 @@ class Model(object): save.alters_data = True - def save_base(self, raw=False, cls=None, force_insert=False, + def save_base(self, raw=False, cls=None, origin=None, force_insert=False, force_update=False, using=None): """ Does the heavy-lifting involved in saving. Subclasses shouldn't need to override this method. It's separate from save() in order to hide the need for overrides of save() to pass around internal-only parameters - ('raw' and 'cls'). + ('raw', 'cls', and 'origin'). """ if using is None: using = DEFAULT_DB_ALIAS connection = connections[using] assert not (force_insert and force_update) - if not cls: + if cls is None: cls = self.__class__ - meta = self._meta - signal = True - signals.pre_save.send(sender=self.__class__, instance=self, raw=raw) + meta = cls._meta + if not meta.proxy: + origin = cls else: meta = cls._meta - signal = False + + if origin: + signals.pre_save.send(sender=origin, instance=self, raw=raw) # If we are in a raw save, save the object exactly as presented. # That means that we don't try to be smart about saving attributes # that might have come from the parent class - we just save the # attributes we have been given to the class we have been given. - if not raw: + # We also go through this process to defer the save of proxy objects + # to their actual underlying model. + if not raw or meta.proxy: + if meta.proxy: + org = cls + else: + org = None for parent, field in meta.parents.items(): # At this point, parent's primary key field may be unknown # (for example, from administration form which doesn't fill @@ -444,7 +452,8 @@ class Model(object): if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) - self.save_base(cls=parent, using=using) + self.save_base(cls=parent, origin=org, using=using) + if field: setattr(self, field.attname, self._get_pk_val(parent._meta)) if meta.proxy: @@ -496,8 +505,8 @@ class Model(object): setattr(self, meta.pk.attname, result) transaction.commit_unless_managed(using=using) - if signal: - signals.post_save.send(sender=self.__class__, instance=self, + if origin: + signals.post_save.send(sender=origin, instance=self, created=(not record_exists), raw=raw) save_base.alters_data = True diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 2adbaffca8..4e2c962cbc 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -132,12 +132,13 @@ class RelatedField(object): v, field = getattr(v, v._meta.pk.name), v._meta.pk except AttributeError: pass - if field: - if lookup_type in ('range', 'in'): - v = [v] - v = field.get_db_prep_lookup(lookup_type, v) - if isinstance(v, list): - v = v[0] + if not field: + field = self.rel.get_related_field() + if lookup_type in ('range', 'in'): + v = [v] + v = field.get_db_prep_lookup(lookup_type, v) + if isinstance(v, list): + v = v[0] return v if hasattr(value, 'as_sql') or hasattr(value, '_as_sql'): diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt index 07aa477d67..28ace85ca8 100644 --- a/docs/intro/tutorial04.txt +++ b/docs/intro/tutorial04.txt @@ -20,7 +20,7 @@ tutorial, so that the template contains an HTML ``
`` element: {% if error_message %}

{{ error_message }}

{% endif %} - + {% for choice in poll.choice_set.all %}
@@ -36,12 +36,12 @@ A quick rundown: selects one of the radio buttons and submits the form, it'll send the POST data ``choice=3``. This is HTML Forms 101. - * We set the form's ``action`` to ``vote/``, and we set ``method="post"``. - Using ``method="post"`` (as opposed to ``method="get"``) is very - important, because the act of submitting this form will alter data - server-side. Whenever you create a form that alters data server-side, use - ``method="post"``. This tip isn't specific to Django; it's just good Web - development practice. + * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we + set ``method="post"``. Using ``method="post"`` (as opposed to + ``method="get"``) is very important, because the act of submitting this + form will alter data server-side. Whenever you create a form that alters + data server-side, use ``method="post"``. This tip isn't specific to + Django; it's just good Web development practice. * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone through its loop @@ -173,11 +173,11 @@ bunch of our own code. We'll just have to take a few steps to make the conversion. We will: 1. Convert the URLconf. - + 2. Rename a few templates. - + 3. Delete some the old, now unneeded views. - + 4. Fix up URL handling for the new views. Read on for details. diff --git a/docs/ref/files/storage.txt b/docs/ref/files/storage.txt index 0ca577059e..c8aafa8626 100644 --- a/docs/ref/files/storage.txt +++ b/docs/ref/files/storage.txt @@ -43,8 +43,8 @@ modify the filename as necessary to get a unique name. The actual name of the stored file will be returned. The ``content`` argument must be an instance of -:class:`django.db.files.File` or of a subclass of -:class:`~django.db.files.File`. +:class:`django.core.files.File` or of a subclass of +:class:`~django.core.files.File`. ``Storage.delete(name)`` ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 2ec74e4306..177df12862 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -800,21 +800,22 @@ you can use the name of the model, rather than the model object itself:: class Manufacturer(models.Model): # ... -Note, however, that this only refers to models in the same ``models.py`` file -- -you cannot use a string to reference a model defined in another application or -imported from elsewhere. +.. versionadded:: 1.0 -.. versionchanged:: 1.0 - Refering models in other applications must include the application label. - -To refer to models defined in another -application, you must instead explicitly specify the application label. For -example, if the ``Manufacturer`` model above is defined in another application -called ``production``, you'd need to use:: +To refer to models defined in another application, you can explicitly specify +a model with the full application label. For example, if the ``Manufacturer`` +model above is defined in another application called ``production``, you'd +need to use:: class Car(models.Model): manufacturer = models.ForeignKey('production.Manufacturer') +This sort of reference can be useful when resolving circular import +dependencies between two applications. + +Database Representation +~~~~~~~~~~~~~~~~~~~~~~~ + Behind the scenes, Django appends ``"_id"`` to the field name to create its database column name. In the above example, the database table for the ``Car`` model will have a ``manufacturer_id`` column. (You can change this explicitly by @@ -824,6 +825,9 @@ deal with the field names of your model object. .. _foreign-key-arguments: +Arguments +~~~~~~~~~ + :class:`ForeignKey` accepts an extra set of arguments -- all optional -- that define the details of how the relation works. @@ -871,6 +875,9 @@ the model is related. This works exactly the same as it does for :class:`ForeignKey`, including all the options regarding :ref:`recursive ` and :ref:`lazy ` relationships. +Database Representation +~~~~~~~~~~~~~~~~~~~~~~~ + Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship. By default, this table name is generated using the names of the two tables being joined. Since some databases don't support table @@ -882,6 +889,9 @@ You can manually provide the name of the join table using the .. _manytomany-arguments: +Arguments +~~~~~~~~~ + :class:`ManyToManyField` accepts an extra set of arguments -- all optional -- that control how the relationship functions. diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index 370ac887b7..690ca7f391 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -323,16 +323,19 @@ Since the Author model has only 3 fields, 'name', 'title', and to be empty, and does not provide a default value for the missing fields, any attempt to ``save()`` a ``ModelForm`` with missing fields will fail. To avoid this failure, you must instantiate your model with initial values - for the missing, but required fields, or use ``save(commit=False)`` and - manually set any extra required fields:: + for the missing, but required fields:: - instance = Instance(required_field='value') - form = InstanceForm(request.POST, instance=instance) - new_instance = form.save() + author = Author(title='Mr') + form = PartialAuthorForm(request.POST, instance=author) + form.save() - instance = form.save(commit=False) - instance.required_field = 'new value' - new_instance = instance.save() + Alternatively, you can use ``save(commit=False)`` and manually set + any extra required fields:: + + form = PartialAuthorForm(request.POST) + author = form.save(commit=False) + author.title = 'Mr' + author.save() See the `section on saving forms`_ for more details on using ``save(commit=False)``. @@ -563,8 +566,8 @@ number of objects needed:: >>> formset.initial [{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}] -If the value of ``max_num`` is higher than the number of objects returned, up to -``extra`` additional blank forms will be added to the formset, so long as the +If the value of ``max_num`` is higher than the number of objects returned, up to +``extra`` additional blank forms will be added to the formset, so long as the total number of forms does not exceed ``max_num``:: >>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=2) diff --git a/tests/modeltests/custom_pk/fields.py b/tests/modeltests/custom_pk/fields.py new file mode 100644 index 0000000000..319e42f974 --- /dev/null +++ b/tests/modeltests/custom_pk/fields.py @@ -0,0 +1,54 @@ +import random +import string + +from django.db import models + +class MyWrapper(object): + def __init__(self, value): + self.value = value + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self.value) + + def __unicode__(self): + return self.value + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.value == other.value + return self.value == other + +class MyAutoField(models.CharField): + __metaclass__ = models.SubfieldBase + + def __init__(self, *args, **kwargs): + kwargs['max_length'] = 10 + super(MyAutoField, self).__init__(*args, **kwargs) + + def pre_save(self, instance, add): + value = getattr(instance, self.attname, None) + if not value: + value = MyWrapper(''.join(random.sample(string.lowercase, 10))) + setattr(instance, self.attname, value) + return value + + def to_python(self, value): + if not value: + return + if not isinstance(value, MyWrapper): + value = MyWrapper(value) + return value + + def get_db_prep_save(self, value): + if not value: + return + if isinstance(value, MyWrapper): + return unicode(value) + return value + + def get_db_prep_value(self, value): + if not value: + return + if isinstance(value, MyWrapper): + return unicode(value) + return value diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py index 091f7f32b4..b1d0cb37d0 100644 --- a/tests/modeltests/custom_pk/models.py +++ b/tests/modeltests/custom_pk/models.py @@ -9,6 +9,8 @@ this behavior by explicitly adding ``primary_key=True`` to a field. from django.conf import settings from django.db import models, transaction, IntegrityError +from fields import MyAutoField + class Employee(models.Model): employee_code = models.IntegerField(primary_key=True, db_column = 'code') first_name = models.CharField(max_length=20) @@ -28,6 +30,16 @@ class Business(models.Model): def __unicode__(self): return self.name +class Bar(models.Model): + id = MyAutoField(primary_key=True, db_index=True) + + def __unicode__(self): + return repr(self.pk) + + +class Foo(models.Model): + bar = models.ForeignKey(Bar) + __test__ = {'API_TESTS':""" >>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones') >>> dan.save() @@ -121,6 +133,21 @@ DoesNotExist: Employee matching query does not exist. ... print "Fail with %s" % type(e) Pass +# Regression for #10785 -- Custom fields can be used for primary keys. +>>> new_bar = Bar.objects.create() +>>> new_foo = Foo.objects.create(bar=new_bar) +>>> f = Foo.objects.get(bar=new_bar.pk) +>>> f == new_foo +True +>>> f.bar == new_bar +True + +>>> f = Foo.objects.get(bar=new_bar) +>>> f == new_foo +True +>>> f.bar == new_bar +True + """} # SQLite lets objects be saved with an empty primary key, even though an diff --git a/tests/modeltests/proxy_models/fixtures/mypeople.json b/tests/modeltests/proxy_models/fixtures/mypeople.json new file mode 100644 index 0000000000..d20c8f2a6e --- /dev/null +++ b/tests/modeltests/proxy_models/fixtures/mypeople.json @@ -0,0 +1,9 @@ +[ + { + "pk": 100, + "model": "proxy_models.myperson", + "fields": { + "name": "Elvis Presley" + } + } +] \ No newline at end of file diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py index 4b3f7d925d..e38266fb70 100644 --- a/tests/modeltests/proxy_models/models.py +++ b/tests/modeltests/proxy_models/models.py @@ -259,6 +259,40 @@ FieldError: Proxy model 'NoNewFields' contains model fields. >>> OtherPerson._default_manager.all() [, ] +# Test save signals for proxy models +>>> from django.db.models import signals +>>> def make_handler(model, event): +... def _handler(*args, **kwargs): +... print u"%s %s save" % (model, event) +... return _handler +>>> h1 = make_handler('MyPerson', 'pre') +>>> h2 = make_handler('MyPerson', 'post') +>>> h3 = make_handler('Person', 'pre') +>>> h4 = make_handler('Person', 'post') +>>> signals.pre_save.connect(h1, sender=MyPerson) +>>> signals.post_save.connect(h2, sender=MyPerson) +>>> signals.pre_save.connect(h3, sender=Person) +>>> signals.post_save.connect(h4, sender=Person) +>>> dino = MyPerson.objects.create(name=u"dino") +MyPerson pre save +MyPerson post save + +# Test save signals for proxy proxy models +>>> h5 = make_handler('MyPersonProxy', 'pre') +>>> h6 = make_handler('MyPersonProxy', 'post') +>>> signals.pre_save.connect(h5, sender=MyPersonProxy) +>>> signals.post_save.connect(h6, sender=MyPersonProxy) +>>> dino = MyPersonProxy.objects.create(name=u"pebbles") +MyPersonProxy pre save +MyPersonProxy post save + +>>> signals.pre_save.disconnect(h1, sender=MyPerson) +>>> signals.post_save.disconnect(h2, sender=MyPerson) +>>> signals.pre_save.disconnect(h3, sender=Person) +>>> signals.post_save.disconnect(h4, sender=Person) +>>> signals.pre_save.disconnect(h5, sender=MyPersonProxy) +>>> signals.post_save.disconnect(h6, sender=MyPersonProxy) + # A proxy has the same content type as the model it is proxying for (at the # storage level, it is meant to be essentially indistinguishable). >>> ctype = ContentType.objects.get_for_model @@ -266,7 +300,7 @@ FieldError: Proxy model 'NoNewFields' contains model fields. True >>> MyPersonProxy.objects.all() -[, ] +[, , , ] >>> u = User.objects.create(name='Bruce') >>> User.objects.all() @@ -327,4 +361,11 @@ True # Select related + filter on a related proxy of proxy field >>> ProxyImprovement.objects.select_related().get(associated_bug__summary__icontains='fix') + +Proxy models can be loaded from fixtures (Regression for #11194) +>>> from django.core import management +>>> management.call_command('loaddata', 'mypeople.json', verbosity=0) +>>> MyPerson.objects.get(pk=100) + + """} diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py index 621c80c15f..16a9fc4fc5 100644 --- a/tests/regressiontests/fixtures_regress/models.py +++ b/tests/regressiontests/fixtures_regress/models.py @@ -9,6 +9,9 @@ class Animal(models.Model): count = models.IntegerField() weight = models.FloatField() + # use a non-default name for the default manager + specimens = models.Manager() + def __unicode__(self): return self.common_name @@ -161,4 +164,10 @@ Weight = 1.2 () >>> models.signals.pre_save.disconnect(animal_pre_save_check) +############################################### +# Regression for #11286 -- Ensure that dumpdata honors the default manager +# Dump the current contents of the database as a JSON fixture +>>> management.call_command('dumpdata', 'fixtures_regress.animal', format='json') +[{"pk": 1, "model": "fixtures_regress.animal", "fields": {"count": 3, "weight": 1.2, "name": "Lion", "latin_name": "Panthera leo"}}, {"pk": 2, "model": "fixtures_regress.animal", "fields": {"count": 2, "weight": 2.29..., "name": "Platypus", "latin_name": "Ornithorhynchus anatinus"}}, {"pk": 10, "model": "fixtures_regress.animal", "fields": {"count": 42, "weight": 1.2, "name": "Emu", "latin_name": "Dromaius novaehollandiae"}}] + """}