1
0
mirror of https://github.com/django/django.git synced 2025-07-06 18:59:13 +00:00

[soc2009/multidb] Merged up to trunk r10973. Resolved merge conflicts

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@10978 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-06-10 23:30:30 +00:00
parent 33ed158dc4
commit 83df1f1056
13 changed files with 221 additions and 55 deletions

View File

@ -73,7 +73,7 @@ class Command(BaseCommand):
model_list = get_models(app) model_list = get_models(app)
for model in model_list: for model in model_list:
objects.extend(model.objects.all()) objects.extend(model._default_manager.all())
try: try:
return serializers.serialize(format, objects, indent=indent) return serializers.serialize(format, objects, indent=indent)

View File

@ -25,6 +25,10 @@ class BaseDatabaseCreation(object):
def __init__(self, connection): def __init__(self, connection):
self.connection = 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()): def sql_create_model(self, model, style, known_models=set()):
""" """
Returns the SQL required to create a single model, as a tuple of: 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 col = opts.get_field(f.rel.field_name).column
# For MySQL, r_name must be unique in the first 64 characters. # For MySQL, r_name must be unique in the first 64 characters.
# So we are careful with character usage here. # 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;' % \ 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_table), qn(truncate_name(r_name, self.connection.ops.max_name_length())),
qn(r_col), qn(table), qn(col), qn(r_col), qn(table), qn(col),
@ -187,8 +191,7 @@ class BaseDatabaseCreation(object):
output.append('\n'.join(table_output)) output.append('\n'.join(table_output))
for r_table, r_col, table, col in deferred: for r_table, r_col, table, col in deferred:
r_name = '%s_refs_%s_%x' % (r_col, col, r_name = '%s_refs_%s_%s' % (r_col, col, self._digest(r_table, table))
abs(hash((r_table, table))))
output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
(qn(r_table), (qn(r_table),
qn(truncate_name(r_name, self.connection.ops.max_name_length())), qn(truncate_name(r_name, self.connection.ops.max_name_length())),
@ -289,7 +292,7 @@ class BaseDatabaseCreation(object):
col = f.column col = f.column
r_table = model._meta.db_table r_table = model._meta.db_table
r_col = model._meta.get_field(f.rel.field_name).column 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;' % \ output.append('%s %s %s %s;' % \
(style.SQL_KEYWORD('ALTER TABLE'), (style.SQL_KEYWORD('ALTER TABLE'),
style.SQL_TABLE(qn(table)), style.SQL_TABLE(qn(table)),

View File

@ -411,32 +411,40 @@ class Model(object):
save.alters_data = True 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): force_update=False, using=None):
""" """
Does the heavy-lifting involved in saving. Subclasses shouldn't need to 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 override this method. It's separate from save() in order to hide the
need for overrides of save() to pass around internal-only parameters need for overrides of save() to pass around internal-only parameters
('raw' and 'cls'). ('raw', 'cls', and 'origin').
""" """
if using is None: if using is None:
using = DEFAULT_DB_ALIAS using = DEFAULT_DB_ALIAS
connection = connections[using] connection = connections[using]
assert not (force_insert and force_update) assert not (force_insert and force_update)
if not cls: if cls is None:
cls = self.__class__ cls = self.__class__
meta = self._meta meta = cls._meta
signal = True if not meta.proxy:
signals.pre_save.send(sender=self.__class__, instance=self, raw=raw) origin = cls
else: else:
meta = cls._meta 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. # 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 means that we don't try to be smart about saving attributes
# that might have come from the parent class - we just save the # that might have come from the parent class - we just save the
# attributes we have been given to the class we have been given. # 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(): for parent, field in meta.parents.items():
# At this point, parent's primary key field may be unknown # At this point, parent's primary key field may be unknown
# (for example, from administration form which doesn't fill # (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: 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)) 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: if field:
setattr(self, field.attname, self._get_pk_val(parent._meta)) setattr(self, field.attname, self._get_pk_val(parent._meta))
if meta.proxy: if meta.proxy:
@ -496,8 +505,8 @@ class Model(object):
setattr(self, meta.pk.attname, result) setattr(self, meta.pk.attname, result)
transaction.commit_unless_managed(using=using) transaction.commit_unless_managed(using=using)
if signal: if origin:
signals.post_save.send(sender=self.__class__, instance=self, signals.post_save.send(sender=origin, instance=self,
created=(not record_exists), raw=raw) created=(not record_exists), raw=raw)
save_base.alters_data = True save_base.alters_data = True

View File

@ -132,7 +132,8 @@ class RelatedField(object):
v, field = getattr(v, v._meta.pk.name), v._meta.pk v, field = getattr(v, v._meta.pk.name), v._meta.pk
except AttributeError: except AttributeError:
pass pass
if field: if not field:
field = self.rel.get_related_field()
if lookup_type in ('range', 'in'): if lookup_type in ('range', 'in'):
v = [v] v = [v]
v = field.get_db_prep_lookup(lookup_type, v) v = field.get_db_prep_lookup(lookup_type, v)

View File

@ -20,7 +20,7 @@ tutorial, so that the template contains an HTML ``<form>`` element:
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="vote/" method="post"> <form action="/polls/{{ poll.id }}/vote/" method="post">
{% for choice in poll.choice_set.all %} {% for choice in poll.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br /> <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
@ -36,12 +36,12 @@ A quick rundown:
selects one of the radio buttons and submits the form, it'll send the selects one of the radio buttons and submits the form, it'll send the
POST data ``choice=3``. This is HTML Forms 101. POST data ``choice=3``. This is HTML Forms 101.
* We set the form's ``action`` to ``vote/``, and we set ``method="post"``. * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we
Using ``method="post"`` (as opposed to ``method="get"``) is very set ``method="post"``. Using ``method="post"`` (as opposed to
important, because the act of submitting this form will alter data ``method="get"``) is very important, because the act of submitting this
server-side. Whenever you create a form that alters data server-side, use form will alter data server-side. Whenever you create a form that alters
``method="post"``. This tip isn't specific to Django; it's just good Web data server-side, use ``method="post"``. This tip isn't specific to
development practice. Django; it's just good Web development practice.
* ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone
through its loop through its loop

View File

@ -43,8 +43,8 @@ modify the filename as necessary to get a unique name. The actual name of the
stored file will be returned. stored file will be returned.
The ``content`` argument must be an instance of The ``content`` argument must be an instance of
:class:`django.db.files.File` or of a subclass of :class:`django.core.files.File` or of a subclass of
:class:`~django.db.files.File`. :class:`~django.core.files.File`.
``Storage.delete(name)`` ``Storage.delete(name)``
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -800,21 +800,22 @@ you can use the name of the model, rather than the model object itself::
class Manufacturer(models.Model): class Manufacturer(models.Model):
# ... # ...
Note, however, that this only refers to models in the same ``models.py`` file -- .. versionadded:: 1.0
you cannot use a string to reference a model defined in another application or
imported from elsewhere.
.. versionchanged:: 1.0 To refer to models defined in another application, you can explicitly specify
Refering models in other applications must include the application label. a model with the full application label. For example, if the ``Manufacturer``
model above is defined in another application called ``production``, you'd
To refer to models defined in another need to use::
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::
class Car(models.Model): class Car(models.Model):
manufacturer = models.ForeignKey('production.Manufacturer') 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 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`` 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 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: .. _foreign-key-arguments:
Arguments
~~~~~~~~~
:class:`ForeignKey` accepts an extra set of arguments -- all optional -- that :class:`ForeignKey` accepts an extra set of arguments -- all optional -- that
define the details of how the relation works. 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 :class:`ForeignKey`, including all the options regarding :ref:`recursive
<recursive-relationships>` and :ref:`lazy <lazy-relationships>` relationships. <recursive-relationships>` and :ref:`lazy <lazy-relationships>` relationships.
Database Representation
~~~~~~~~~~~~~~~~~~~~~~~
Behind the scenes, Django creates an intermediary join table to represent the 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 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 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: .. _manytomany-arguments:
Arguments
~~~~~~~~~
:class:`ManyToManyField` accepts an extra set of arguments -- all optional -- :class:`ManyToManyField` accepts an extra set of arguments -- all optional --
that control how the relationship functions. that control how the relationship functions.

View File

@ -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, 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. any attempt to ``save()`` a ``ModelForm`` with missing fields will fail.
To avoid this failure, you must instantiate your model with initial values To avoid this failure, you must instantiate your model with initial values
for the missing, but required fields, or use ``save(commit=False)`` and for the missing, but required fields::
manually set any extra required fields::
instance = Instance(required_field='value') author = Author(title='Mr')
form = InstanceForm(request.POST, instance=instance) form = PartialAuthorForm(request.POST, instance=author)
new_instance = form.save() form.save()
instance = form.save(commit=False) Alternatively, you can use ``save(commit=False)`` and manually set
instance.required_field = 'new value' any extra required fields::
new_instance = instance.save()
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 See the `section on saving forms`_ for more details on using
``save(commit=False)``. ``save(commit=False)``.

View File

@ -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

View File

@ -9,6 +9,8 @@ this behavior by explicitly adding ``primary_key=True`` to a field.
from django.conf import settings from django.conf import settings
from django.db import models, transaction, IntegrityError from django.db import models, transaction, IntegrityError
from fields import MyAutoField
class Employee(models.Model): class Employee(models.Model):
employee_code = models.IntegerField(primary_key=True, db_column = 'code') employee_code = models.IntegerField(primary_key=True, db_column = 'code')
first_name = models.CharField(max_length=20) first_name = models.CharField(max_length=20)
@ -28,6 +30,16 @@ class Business(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name 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':""" __test__ = {'API_TESTS':"""
>>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones') >>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones')
>>> dan.save() >>> dan.save()
@ -121,6 +133,21 @@ DoesNotExist: Employee matching query does not exist.
... print "Fail with %s" % type(e) ... print "Fail with %s" % type(e)
Pass 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 # SQLite lets objects be saved with an empty primary key, even though an

View File

@ -0,0 +1,9 @@
[
{
"pk": 100,
"model": "proxy_models.myperson",
"fields": {
"name": "Elvis Presley"
}
}
]

View File

@ -259,6 +259,40 @@ FieldError: Proxy model 'NoNewFields' contains model fields.
>>> OtherPerson._default_manager.all() >>> OtherPerson._default_manager.all()
[<OtherPerson: barney>, <OtherPerson: wilma>] [<OtherPerson: barney>, <OtherPerson: wilma>]
# 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 # 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). # storage level, it is meant to be essentially indistinguishable).
>>> ctype = ContentType.objects.get_for_model >>> ctype = ContentType.objects.get_for_model
@ -266,7 +300,7 @@ FieldError: Proxy model 'NoNewFields' contains model fields.
True True
>>> MyPersonProxy.objects.all() >>> MyPersonProxy.objects.all()
[<MyPersonProxy: barney>, <MyPersonProxy: fred>] [<MyPersonProxy: barney>, <MyPersonProxy: dino>, <MyPersonProxy: fred>, <MyPersonProxy: pebbles>]
>>> u = User.objects.create(name='Bruce') >>> u = User.objects.create(name='Bruce')
>>> User.objects.all() >>> User.objects.all()
@ -327,4 +361,11 @@ True
# Select related + filter on a related proxy of proxy field # Select related + filter on a related proxy of proxy field
>>> ProxyImprovement.objects.select_related().get(associated_bug__summary__icontains='fix') >>> ProxyImprovement.objects.select_related().get(associated_bug__summary__icontains='fix')
<ProxyImprovement: ProxyImprovement:improve that> <ProxyImprovement: ProxyImprovement:improve that>
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)
<MyPerson: Elvis Presley>
"""} """}

View File

@ -9,6 +9,9 @@ class Animal(models.Model):
count = models.IntegerField() count = models.IntegerField()
weight = models.FloatField() weight = models.FloatField()
# use a non-default name for the default manager
specimens = models.Manager()
def __unicode__(self): def __unicode__(self):
return self.common_name return self.common_name
@ -161,4 +164,10 @@ Weight = 1.2 (<type 'float'>)
>>> models.signals.pre_save.disconnect(animal_pre_save_check) >>> 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"}}]
"""} """}