[Python-modules-commits] [python-django] 01/03: Imported Upstream version 1.7.6
Raphaël Hertzog
hertzog at moszumanska.debian.org
Mon Mar 9 20:59:07 UTC 2015
This is an automated email from the git hooks/post-receive script.
hertzog pushed a commit to branch debian/sid
in repository python-django.
commit c2ec6c3209ee7f232e8bfca3d922a810bd3078d5
Author: Raphaël Hertzog <hertzog at debian.org>
Date: Mon Mar 9 21:40:21 2015 +0100
Imported Upstream version 1.7.6
---
Django.egg-info/PKG-INFO | 2 +-
Django.egg-info/SOURCES.txt | 1 +
PKG-INFO | 2 +-
django/__init__.py | 2 +-
django/contrib/admin/helpers.py | 2 +-
django/db/backends/schema.py | 6 +-
django/db/models/fields/related.py | 3 +
docs/howto/auth-remote-user.txt | 12 ++++
docs/howto/custom-template-tags.txt | 2 +-
docs/howto/deployment/wsgi/modwsgi.txt | 12 +++-
docs/intro/tutorial01.txt | 4 +-
docs/ref/class-based-views/generic-date-based.txt | 4 --
docs/ref/templates/builtins.txt | 8 ++-
docs/releases/1.7.6.txt | 30 +++++++++
docs/releases/index.txt | 1 +
docs/topics/auth/default.txt | 82 ++++++++++++++++++++---
docs/topics/forms/modelforms.txt | 21 +++++-
tests/admin_views/admin.py | 2 +-
tests/admin_views/models.py | 7 ++
tests/admin_views/tests.py | 9 +++
tests/m2m_regress/tests.py | 4 ++
tests/schema/models.py | 9 +++
tests/schema/tests.py | 36 +++++++++-
23 files changed, 224 insertions(+), 37 deletions(-)
diff --git a/Django.egg-info/PKG-INFO b/Django.egg-info/PKG-INFO
index 8457a3f..21d6b3f 100644
--- a/Django.egg-info/PKG-INFO
+++ b/Django.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: Django
-Version: 1.7.5
+Version: 1.7.6
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: http://www.djangoproject.com/
Author: Django Software Foundation
diff --git a/Django.egg-info/SOURCES.txt b/Django.egg-info/SOURCES.txt
index da86442..f67cbb5 100644
--- a/Django.egg-info/SOURCES.txt
+++ b/Django.egg-info/SOURCES.txt
@@ -4023,6 +4023,7 @@ docs/releases/1.7.2.txt
docs/releases/1.7.3.txt
docs/releases/1.7.4.txt
docs/releases/1.7.5.txt
+docs/releases/1.7.6.txt
docs/releases/1.7.txt
docs/releases/index.txt
docs/releases/security.txt
diff --git a/PKG-INFO b/PKG-INFO
index 8457a3f..21d6b3f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: Django
-Version: 1.7.5
+Version: 1.7.6
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: http://www.djangoproject.com/
Author: Django Software Foundation
diff --git a/django/__init__.py b/django/__init__.py
index 0e4c6e8..77b09d9 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (1, 7, 5, 'final', 0)
+VERSION = (1, 7, 6, 'final', 0)
def get_version(*args, **kwargs):
diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index 4f6bbe5..6ea34e2 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -193,7 +193,7 @@ class AdminReadonlyField(object):
if getattr(attr, "allow_tags", False):
result_repr = mark_safe(result_repr)
else:
- result_repr = linebreaksbr(result_repr)
+ result_repr = linebreaksbr(result_repr, autoescape=True)
else:
if isinstance(f.rel, ManyToManyRel) and value is not None:
result_repr = ", ".join(map(six.text_type, value.all()))
diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py
index 54e6e06..4a5637c 100644
--- a/django/db/backends/schema.py
+++ b/django/db/backends/schema.py
@@ -675,9 +675,9 @@ class BaseDatabaseSchemaEditor(object):
}
)
# Does it have a foreign key?
- if new_field.rel and \
- (fks_dropped or (old_field.rel and not old_field.db_constraint)) and \
- new_field.db_constraint:
+ if (new_field.rel and
+ (fks_dropped or not old_field.rel or not old_field.db_constraint) and
+ new_field.db_constraint):
self.execute(self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s"))
# Rebuild FKs that pointed to us if we previously had to drop them
if old_field.primary_key and new_field.primary_key and old_type != new_type:
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 344b238..c4cf4b7 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -855,6 +855,9 @@ def create_many_related_manager(superclass, rel):
)
do_not_call_in_templates = True
+ def __str__(self):
+ return repr(self)
+
def _build_remove_filters(self, removed_vals):
filters = Q(**{self.source_field_name: self.related_val})
# No need to add a subquery condition if removed_vals is a QuerySet without
diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt
index dc96a98..8d1d44a 100644
--- a/docs/howto/auth-remote-user.txt
+++ b/docs/howto/auth-remote-user.txt
@@ -50,6 +50,18 @@ With this setup, ``RemoteUserMiddleware`` will detect the username in
``request.META['REMOTE_USER']`` and will authenticate and auto-login that user
using the :class:`~django.contrib.auth.backends.RemoteUserBackend`.
+Be aware that this particular setup disables authentication with the default
+``ModelBackend``. This means that if the ``REMOTE_USER`` value is not set
+then the user is unable to log in, even using Django's admin interface.
+Adding ``'django.contrib.auth.backends.ModelBackend'`` to the
+``AUTHENTICATION_BACKENDS`` list will use ``ModelBackend`` as a fallback
+if ``REMOTE_USER`` is absent, which will solve these issues.
+
+Django's user management, such as the views in ``contrib.admin`` and
+the :djadmin:`createsuperuser` management command, doesn't integrate with
+remote users. These interfaces work with users stored in the database
+regardless of ``AUTHENTICATION_BACKENDS``.
+
.. note::
Since the ``RemoteUserBackend`` inherits from ``ModelBackend``, you will
still have all of the same permissions checking that is implemented in
diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt
index 8132634..00bf7b1 100644
--- a/docs/howto/custom-template-tags.txt
+++ b/docs/howto/custom-template-tags.txt
@@ -290,7 +290,7 @@ Template filter code falls into one of two situations:
Be careful, though. You need to do more than just mark the output as
safe. You need to ensure it really *is* safe, and what you do depends on
- whether auto-escaping is in effect. The idea is to write filters than
+ whether auto-escaping is in effect. The idea is to write filters that
can operate in templates where auto-escaping is either on or off in
order to make things easier for your template authors.
diff --git a/docs/howto/deployment/wsgi/modwsgi.txt b/docs/howto/deployment/wsgi/modwsgi.txt
index 042555e..d994ef1 100644
--- a/docs/howto/deployment/wsgi/modwsgi.txt
+++ b/docs/howto/deployment/wsgi/modwsgi.txt
@@ -83,7 +83,9 @@ your Python path as well. To do this, add an additional path to your
``WSGIPythonPath`` directive, with multiple paths separated by a colon (``:``)
if using a UNIX-like system, or a semicolon (``;``) if using Windows. If any
part of a directory path contains a space character, the complete argument
-string to ``WSGIPythonPath`` must be quoted::
+string to ``WSGIPythonPath`` must be quoted:
+
+.. code-block:: apache
WSGIPythonPath /path/to/mysite.com:/path/to/your/venv/lib/python2.X/site-packages
@@ -103,7 +105,9 @@ Django instance to run in it, you will need to add appropriate
``WSGIDaemonProcess`` and ``WSGIProcessGroup`` directives. A further change
required to the above configuration if you use daemon mode is that you can't
use ``WSGIPythonPath``; instead you should use the ``python-path`` option to
-``WSGIDaemonProcess``, for example::
+``WSGIDaemonProcess``, for example:
+
+.. code-block:: apache
WSGIDaemonProcess example.com python-path=/path/to/mysite.com:/path/to/venv/lib/python2.7/site-packages
WSGIProcessGroup example.com
@@ -137,7 +141,9 @@ static media, and others using the mod_wsgi interface to Django.
This example sets up Django at the site root, but explicitly serves
``robots.txt``, ``favicon.ico``, any CSS file, and anything in the
``/static/`` and ``/media/`` URL space as a static file. All other URLs
-will be served using mod_wsgi::
+will be served using mod_wsgi:
+
+.. code-block:: apache
Alias /robots.txt /path/to/mysite.com/static/robots.txt
Alias /favicon.ico /path/to/mysite.com/static/favicon.ico
diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt
index 0a80c84..6f4a307 100644
--- a/docs/intro/tutorial01.txt
+++ b/docs/intro/tutorial01.txt
@@ -670,8 +670,8 @@ of this object. Let's fix that by editing the ``Question`` model (in the
return self.choice_text
It's important to add :meth:`~django.db.models.Model.__str__` methods to your
-models, not only for your own sanity when dealing with the interactive prompt,
-but also because objects' representations are used throughout Django's
+models, not only for your own convenience when dealing with the interactive
+prompt, but also because objects' representations are used throughout Django's
automatically-generated admin.
.. admonition:: ``__str__`` or ``__unicode__``?
diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt
index c772443..d9b131e 100644
--- a/docs/ref/class-based-views/generic-date-based.txt
+++ b/docs/ref/class-based-views/generic-date-based.txt
@@ -256,7 +256,6 @@ MonthArchiveView
class ArticleMonthArchiveView(MonthArchiveView):
queryset = Article.objects.all()
date_field = "pub_date"
- make_object_list = True
allow_future = True
**Example myapp/urls.py**::
@@ -349,7 +348,6 @@ WeekArchiveView
class ArticleWeekArchiveView(WeekArchiveView):
queryset = Article.objects.all()
date_field = "pub_date"
- make_object_list = True
week_format = "%W"
allow_future = True
@@ -464,7 +462,6 @@ DayArchiveView
class ArticleDayArchiveView(DayArchiveView):
queryset = Article.objects.all()
date_field = "pub_date"
- make_object_list = True
allow_future = True
**Example myapp/urls.py**::
@@ -538,7 +535,6 @@ TodayArchiveView
class ArticleTodayArchiveView(TodayArchiveView):
queryset = Article.objects.all()
date_field = "pub_date"
- make_object_list = True
allow_future = True
**Example myapp/urls.py**::
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index a68b302..823e958 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -2608,6 +2608,8 @@ get_media_prefix
Similar to the :ttag:`get_static_prefix`, ``get_media_prefix`` populates a
template variable with the media prefix :setting:`MEDIA_URL`, e.g.::
- <script type="text/javascript" charset="utf-8">
- var media_path = '{% get_media_prefix %}';
- </script>
+ {% load static %}
+ <body data-media-url="{% get_media_prefix %}">
+
+By storing the value in a data attribute, we ensure it's escaped appropriately
+if we want to use it in a JavaScript context.
diff --git a/docs/releases/1.7.6.txt b/docs/releases/1.7.6.txt
new file mode 100644
index 0000000..7f54da1
--- /dev/null
+++ b/docs/releases/1.7.6.txt
@@ -0,0 +1,30 @@
+==========================
+Django 1.7.6 release notes
+==========================
+
+*March 9, 2015*
+
+Django 1.7.6 fixes a security issue and several bugs in 1.7.5.
+
+Mitigated an XSS attack via properties in ``ModelAdmin.readonly_fields``
+========================================================================
+
+The :attr:`ModelAdmin.readonly_fields
+<django.contrib.admin.ModelAdmin.readonly_fields>` attribute in the Django
+admin allows displaying model fields and model attributes. While the former
+were correctly escaped, the latter were not. Thus untrusted content could be
+injected into the admin, presenting an exploitation vector for XSS attacks.
+
+In this vulnerability, every model attribute used in ``readonly_fields`` that
+is not an actual model field (e.g. a :class:`property`) will **fail to be
+escaped** even if that attribute is not marked as safe. In this release,
+autoescaping is now correctly applied.
+
+Bugfixes
+========
+
+* Fixed crash when coercing ``ManyRelatedManager`` to a string
+ (:ticket:`24352`).
+
+* Fixed a bug that prevented migrations from adding a foreign key constraint
+ when converting an existing field to a foreign key (:ticket:`24447`).
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index d1dd0bf..e1cf808 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 1.7.6
1.7.5
1.7.4
1.7.3
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
index ba9516d..89af55f 100644
--- a/docs/topics/auth/default.txt
+++ b/docs/topics/auth/default.txt
@@ -356,8 +356,8 @@ If you have an authenticated user you want to attach to the current session
.. admonition:: Calling ``authenticate()`` first
- When you're manually logging a user in, you *must* call
- :func:`~django.contrib.auth.authenticate()` before you call
+ When you're manually logging a user in, you *must* successfully authenticate
+ the user with :func:`~django.contrib.auth.authenticate()` before you call
:func:`~django.contrib.auth.login()`.
:func:`~django.contrib.auth.authenticate()`
sets an attribute on the :class:`~django.contrib.auth.models.User` noting
@@ -664,18 +664,78 @@ Django provides several views that you can use for handling login, logout, and
password management. These make use of the :ref:`stock auth forms
<built-in-auth-forms>` but you can pass in your own forms as well.
-Django provides no default template for the authentication views - however the
-template context is documented for each view below.
+Django provides no default template for the authentication views. You should
+create your own templates for the views you want to use. The template context
+is documented in each view, see :ref:`all-authentication-views`.
-The built-in views all return
-a :class:`~django.template.response.TemplateResponse` instance, which allows
-you to easily customize the response data before rendering. For more details,
-see the :doc:`TemplateResponse documentation </ref/template-response>`.
+.. _using-the-views:
-Most built-in authentication views provide a URL name for easier reference. See
-:doc:`the URL documentation </topics/http/urls>` for details on using named URL
-patterns.
+Using the views
+~~~~~~~~~~~~~~~
+There are different methods to implement these views in your project. The
+easiest way is to include the provided URLconf in ``django.contrib.auth.urls``
+in your own URLconf, for example::
+
+ urlpatterns = [
+ url('^', include('django.contrib.auth.urls'))
+ ]
+
+This will include the following URL patterns::
+
+ ^login/$ [name='login']
+ ^logout/$ [name='logout']
+ ^password_change/$ [name='password_change']
+ ^password_change/done/$ [name='password_change_done']
+ ^password_reset/$ [name='password_reset']
+ ^password_reset/done/$ [name='password_reset_done']
+ ^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ [name='password_reset_confirm']
+ ^reset/done/$ [name='password_reset_complete']
+
+The views provide a URL name for easier reference. See :doc:`the URL
+documentation </topics/http/urls>` for details on using named URL patterns.
+
+If you want more control over your URLs, you can reference a specific view in
+your URLconf::
+
+ urlpatterns = [
+ url('^change-password/', 'django.contrib.auth.views.password_change')
+ ]
+
+The views have optional arguments you can use to alter the behavior of the
+view. For example, if you want to change the template name a view uses, you can
+provide the ``template_name`` argument. A way to do this is to provide keyword
+arguments in the URLconf, these will be passed on to the view. For example::
+
+ urlpatterns = [
+ url(
+ '^change-password/',
+ 'django.contrib.auth.views.password_change',
+ {'template_name': 'change-password.html'}
+ )
+ ]
+
+All views return a :class:`~django.template.response.TemplateResponse`
+instance, which allows you to easily customize the response data before
+rendering. A way to do this is to wrap a view in your own view::
+
+ from django.contrib.auth import views
+
+ def change_password(request):
+ template_response = views.password_change(request)
+ # Do something with `template_response`
+ return template_response
+
+For more details, see the :doc:`TemplateResponse documentation
+</ref/template-response>`.
+
+.. _all-authentication-views:
+
+All authentication views
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is a list with all the views ``django.contrib.auth`` provides. For
+implementation details see :ref:`using-the-views`.
.. function:: login(request, [template_name, redirect_field_name, authentication_form, current_app, extra_context])
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index 57934df..223f740 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -232,7 +232,7 @@ Overriding the clean() method
You can override the ``clean()`` method on a model form to provide additional
validation in the same way you can on a normal form.
-A model form instance bound to a model object will contain an ``instance``
+A model form instance attached to a model object will contain an ``instance``
attribute that gives its methods access to that specific model instance.
.. warning::
@@ -681,6 +681,21 @@ There are a couple of things to note, however.
a default field. To opt-out from default fields, see
:ref:`controlling-fields-with-fields-and-exclude`.
+Providing initial values
+------------------------
+
+As with regular forms, it's possible to specify initial data for forms by
+specifying an ``initial`` parameter when instantiating the form. Initial
+values provided this way will override both initial values from the form field
+and values from an attached model instance. For example::
+
+ >>> article = Article.objects.get(pk1=)
+ >>> article.headline
+ 'My headline'
+ >>> form = ArticleForm(initial={'headline': 'Initial headline'), instance=article)
+ >>> form['pub_date'].value()
+ 'Initial headline'
+
.. _modelforms-factory:
ModelForm factory function
@@ -853,8 +868,8 @@ As with regular formsets, it's possible to :ref:`specify initial data
<formsets-initial-data>` for forms in the formset by specifying an ``initial``
parameter when instantiating the model formset class returned by
:func:`~django.forms.models.modelformset_factory`. However, with model
-formsets, the initial values only apply to extra forms, those that aren't bound
-to an existing object instance.
+formsets, the initial values only apply to extra forms, those that aren't
+attached to an existing model instance.
.. _saving-objects-in-the-formset:
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 9a05df6..15c1384 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -860,7 +860,7 @@ class GetFormsetsArgumentCheckingAdmin(admin.ModelAdmin):
site = admin.AdminSite(name="admin")
site.register(Article, ArticleAdmin)
site.register(CustomArticle, CustomArticleAdmin)
-site.register(Section, save_as=True, inlines=[ArticleInline])
+site.register(Section, save_as=True, inlines=[ArticleInline], readonly_fields=['name_property'])
site.register(ModelWithStringPrimaryKey)
site.register(Color)
site.register(Thing, ThingAdmin)
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 341b7ae..f41b4de 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -22,6 +22,13 @@ class Section(models.Model):
"""
name = models.CharField(max_length=100)
+ @property
+ def name_property(self):
+ """
+ A property that simply returns the name. Used to test #24461
+ """
+ return self.name
+
@python_2_unicode_compatible
class Article(models.Model):
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 96da7c4..e610642 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -3862,6 +3862,15 @@ class ReadonlyTest(TestCase):
self.assertContains(response, '<label for="id_public">Overridden public label:</label>', html=True)
self.assertNotContains(response, "Some help text for the date (with unicode ŠĐĆŽćžšđ)")
+ def test_correct_autoescaping(self):
+ """
+ Make sure that non-field readonly elements are properly autoescaped (#24461)
+ """
+ section = Section.objects.create(name='<a>evil</a>')
+ response = self.client.get(reverse('admin:admin_views_section_change', args=(section.pk,)))
+ self.assertNotContains(response, "<a>evil</a>", status_code=200)
+ self.assertContains(response, "<a>evil</a>", status_code=200)
+
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class LimitChoicesToInAdminTest(TestCase):
diff --git a/tests/m2m_regress/tests.py b/tests/m2m_regress/tests.py
index d1f85ac..a1431d0 100644
--- a/tests/m2m_regress/tests.py
+++ b/tests/m2m_regress/tests.py
@@ -109,3 +109,7 @@ class M2MRegressionTests(TestCase):
worksheet.lines = hi
self.assertEqual(1, worksheet.lines.count())
self.assertEqual(1, hi.count())
+
+ def test_many_related_manager_str(self):
+ worksheet = Worksheet.objects.create(id=1)
+ self.assertIn('ManyRelatedManager', str(worksheet.lines))
diff --git a/tests/schema/models.py b/tests/schema/models.py
index 6eba7cc..b933175 100644
--- a/tests/schema/models.py
+++ b/tests/schema/models.py
@@ -87,6 +87,15 @@ class BookWithM2M(models.Model):
apps = new_apps
+class BookWithoutFK(models.Model):
+ author = models.IntegerField()
+ title = models.CharField(max_length=100, db_index=True)
+ pub_date = models.DateTimeField()
+
+ class Meta:
+ apps = new_apps
+
+
class TagThrough(models.Model):
book = models.ForeignKey("schema.BookWithM2MThrough")
tag = models.ForeignKey("schema.TagM2MTest")
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 996cec1..cc95d55 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -11,7 +11,7 @@ from .fields import CustomManyToManyField, InheritedManyToManyField
from .models import (Author, AuthorWithDefaultHeight, AuthorWithM2M, Book, BookWithLongName,
BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
- AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O)
+ AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O, BookWithoutFK)
class SchemaTests(TransactionTestCase):
@@ -29,7 +29,7 @@ class SchemaTests(TransactionTestCase):
Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug,
BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest,
Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName,
- BookWeak, BookWithO2O,
+ BookWeak, BookWithO2O, BookWithoutFK,
]
# Utility functions
@@ -537,6 +537,38 @@ class SchemaTests(TransactionTestCase):
self.fail("No FK constraint for author_id found")
@unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
+ def test_alter_to_fk(self):
+ """
+ #24447 - Tests adding a FK constraint for an existing column
+ """
+ # Create the tables
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+ editor.create_model(BookWithoutFK)
+ # Ensure no FK constraint exists
+ constraints = self.get_constraints(BookWithoutFK._meta.db_table)
+ for name, details in constraints.items():
+ if details['foreign_key']:
+ self.fail('Found an unexpected FK constraint to %s' % details['columns'])
+ new_field = ForeignKey(Author)
+ new_field.set_attributes_from_name("author")
+ with connection.schema_editor() as editor:
+ editor.alter_field(
+ BookWithoutFK,
+ BookWithoutFK._meta.get_field_by_name("author")[0],
+ new_field,
+ strict=True,
+ )
+ constraints = self.get_constraints(BookWithoutFK._meta.db_table)
+ # Ensure FK constraint exists
+ for name, details in constraints.items():
+ if details['foreign_key'] and details['columns'] == ["author_id"]:
+ self.assertEqual(details['foreign_key'], ('schema_author', 'id'))
+ break
+ else:
+ self.fail("No FK constraint for author_id found")
+
+ @unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
def test_alter_o2o_to_fk(self):
"""
#24163 - Tests altering of OneToOneField to ForeignKey
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django.git
More information about the Python-modules-commits
mailing list