Moved foreign_object models.py into a module.
This commit is contained in:
parent
5b5a27942b
commit
825429c1f7
|
@ -0,0 +1,9 @@
|
||||||
|
from .article import (
|
||||||
|
Article, ArticleIdea, ArticleTag, ArticleTranslation, NewsArticle,
|
||||||
|
)
|
||||||
|
from .person import Country, Friendship, Group, Membership, Person
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'Article', 'ArticleIdea', 'ArticleTag', 'ArticleTranslation', 'Country',
|
||||||
|
'Friendship', 'Group', 'Membership', 'NewsArticle', 'Person',
|
||||||
|
]
|
|
@ -0,0 +1,99 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models.fields.related import \
|
||||||
|
ReverseSingleRelatedObjectDescriptor
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
from django.utils.translation import get_language
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor):
|
||||||
|
"""
|
||||||
|
The set of articletranslation should not set any local fields.
|
||||||
|
"""
|
||||||
|
def __set__(self, instance, value):
|
||||||
|
if instance is None:
|
||||||
|
raise AttributeError("%s must be accessed via instance" % self.field.name)
|
||||||
|
setattr(instance, self.cache_name, value)
|
||||||
|
if value is not None and not self.field.remote_field.multiple:
|
||||||
|
setattr(value, self.field.related.get_cache_name(), instance)
|
||||||
|
|
||||||
|
|
||||||
|
class ColConstraint(object):
|
||||||
|
# Anything with as_sql() method works in get_extra_restriction().
|
||||||
|
def __init__(self, alias, col, value):
|
||||||
|
self.alias, self.col, self.value = alias, col, value
|
||||||
|
|
||||||
|
def as_sql(self, compiler, connection):
|
||||||
|
qn = compiler.quote_name_unless_alias
|
||||||
|
return '%s.%s = %%s' % (qn(self.alias), qn(self.col)), [self.value]
|
||||||
|
|
||||||
|
|
||||||
|
class ActiveTranslationField(models.ForeignObject):
|
||||||
|
"""
|
||||||
|
This field will allow querying and fetching the currently active translation
|
||||||
|
for Article from ArticleTranslation.
|
||||||
|
"""
|
||||||
|
requires_unique_target = False
|
||||||
|
|
||||||
|
def get_extra_restriction(self, where_class, alias, related_alias):
|
||||||
|
return ColConstraint(alias, 'lang', get_language())
|
||||||
|
|
||||||
|
def get_extra_descriptor_filter(self, instance):
|
||||||
|
return {'lang': get_language()}
|
||||||
|
|
||||||
|
def contribute_to_class(self, cls, name):
|
||||||
|
super(ActiveTranslationField, self).contribute_to_class(cls, name)
|
||||||
|
setattr(cls, self.name, ArticleTranslationDescriptor(self))
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class Article(models.Model):
|
||||||
|
active_translation = ActiveTranslationField(
|
||||||
|
'ArticleTranslation',
|
||||||
|
from_fields=['id'],
|
||||||
|
to_fields=['article'],
|
||||||
|
related_name='+',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
pub_date = models.DateField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
try:
|
||||||
|
return self.active_translation.title
|
||||||
|
except ArticleTranslation.DoesNotExist:
|
||||||
|
return '[No translation found]'
|
||||||
|
|
||||||
|
|
||||||
|
class NewsArticle(Article):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleTranslation(models.Model):
|
||||||
|
article = models.ForeignKey(Article, models.CASCADE)
|
||||||
|
lang = models.CharField(max_length=2)
|
||||||
|
title = models.CharField(max_length=100)
|
||||||
|
body = models.TextField()
|
||||||
|
abstract = models.CharField(max_length=400, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('article', 'lang')
|
||||||
|
ordering = ('active_translation__title',)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleTag(models.Model):
|
||||||
|
article = models.ForeignKey(
|
||||||
|
Article,
|
||||||
|
models.CASCADE,
|
||||||
|
related_name='tags',
|
||||||
|
related_query_name='tag',
|
||||||
|
)
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleIdea(models.Model):
|
||||||
|
articles = models.ManyToManyField(
|
||||||
|
Article,
|
||||||
|
related_name='ideas',
|
||||||
|
related_query_name='idea_things',
|
||||||
|
)
|
||||||
|
name = models.CharField(max_length=255)
|
|
@ -1,10 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import \
|
|
||||||
ReverseSingleRelatedObjectDescriptor
|
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import get_language
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
|
@ -112,89 +109,3 @@ class Friendship(models.Model):
|
||||||
related_name='to_friend',
|
related_name='to_friend',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor):
|
|
||||||
"""
|
|
||||||
The set of articletranslation should not set any local fields.
|
|
||||||
"""
|
|
||||||
def __set__(self, instance, value):
|
|
||||||
if instance is None:
|
|
||||||
raise AttributeError("%s must be accessed via instance" % self.field.name)
|
|
||||||
setattr(instance, self.cache_name, value)
|
|
||||||
if value is not None and not self.field.remote_field.multiple:
|
|
||||||
setattr(value, self.field.related.get_cache_name(), instance)
|
|
||||||
|
|
||||||
|
|
||||||
class ColConstraint(object):
|
|
||||||
# Anything with as_sql() method works in get_extra_restriction().
|
|
||||||
def __init__(self, alias, col, value):
|
|
||||||
self.alias, self.col, self.value = alias, col, value
|
|
||||||
|
|
||||||
def as_sql(self, compiler, connection):
|
|
||||||
qn = compiler.quote_name_unless_alias
|
|
||||||
return '%s.%s = %%s' % (qn(self.alias), qn(self.col)), [self.value]
|
|
||||||
|
|
||||||
|
|
||||||
class ActiveTranslationField(models.ForeignObject):
|
|
||||||
"""
|
|
||||||
This field will allow querying and fetching the currently active translation
|
|
||||||
for Article from ArticleTranslation.
|
|
||||||
"""
|
|
||||||
requires_unique_target = False
|
|
||||||
|
|
||||||
def get_extra_restriction(self, where_class, alias, related_alias):
|
|
||||||
return ColConstraint(alias, 'lang', get_language())
|
|
||||||
|
|
||||||
def get_extra_descriptor_filter(self, instance):
|
|
||||||
return {'lang': get_language()}
|
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
|
||||||
super(ActiveTranslationField, self).contribute_to_class(cls, name)
|
|
||||||
setattr(cls, self.name, ArticleTranslationDescriptor(self))
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Article(models.Model):
|
|
||||||
active_translation = ActiveTranslationField(
|
|
||||||
'ArticleTranslation',
|
|
||||||
from_fields=['id'],
|
|
||||||
to_fields=['article'],
|
|
||||||
related_name='+',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
null=True,
|
|
||||||
)
|
|
||||||
pub_date = models.DateField()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
try:
|
|
||||||
return self.active_translation.title
|
|
||||||
except ArticleTranslation.DoesNotExist:
|
|
||||||
return '[No translation found]'
|
|
||||||
|
|
||||||
|
|
||||||
class NewsArticle(Article):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ArticleTranslation(models.Model):
|
|
||||||
article = models.ForeignKey(Article, models.CASCADE)
|
|
||||||
lang = models.CharField(max_length=2)
|
|
||||||
title = models.CharField(max_length=100)
|
|
||||||
body = models.TextField()
|
|
||||||
abstract = models.CharField(max_length=400, null=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ('article', 'lang')
|
|
||||||
ordering = ('active_translation__title',)
|
|
||||||
|
|
||||||
|
|
||||||
class ArticleTag(models.Model):
|
|
||||||
article = models.ForeignKey(Article, models.CASCADE, related_name="tags", related_query_name="tag")
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
|
|
||||||
|
|
||||||
class ArticleIdea(models.Model):
|
|
||||||
articles = models.ManyToManyField(Article, related_name="ideas",
|
|
||||||
related_query_name="idea_things")
|
|
||||||
name = models.CharField(max_length=255)
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from .models import Article
|
||||||
|
|
||||||
|
|
||||||
|
class FormsTests(TestCase):
|
||||||
|
# ForeignObjects should not have any form fields, currently the user needs
|
||||||
|
# to manually deal with the foreignobject relation.
|
||||||
|
class ArticleForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Article
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def test_foreign_object_form(self):
|
||||||
|
# A very crude test checking that the non-concrete fields do not get form fields.
|
||||||
|
form = FormsTests.ArticleForm()
|
||||||
|
self.assertIn('id_pub_date', form.as_table())
|
||||||
|
self.assertNotIn('active_translation', form.as_table())
|
||||||
|
form = FormsTests.ArticleForm(data={'pub_date': str(datetime.date.today())})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
a = form.save()
|
||||||
|
self.assertEqual(a.pub_date, datetime.date.today())
|
||||||
|
form = FormsTests.ArticleForm(instance=a, data={'pub_date': '2013-01-01'})
|
||||||
|
a2 = form.save()
|
||||||
|
self.assertEqual(a.pk, a2.pk)
|
||||||
|
self.assertEqual(a2.pub_date, datetime.date(2013, 1, 1))
|
|
@ -1,7 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, skipUnlessDBFeature
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
@ -392,26 +391,3 @@ class MultiColumnFKTests(TestCase):
|
||||||
""" See: https://code.djangoproject.com/ticket/21566 """
|
""" See: https://code.djangoproject.com/ticket/21566 """
|
||||||
objs = [Person(name="abcd_%s" % i, person_country=self.usa) for i in range(0, 5)]
|
objs = [Person(name="abcd_%s" % i, person_country=self.usa) for i in range(0, 5)]
|
||||||
Person.objects.bulk_create(objs, 10)
|
Person.objects.bulk_create(objs, 10)
|
||||||
|
|
||||||
|
|
||||||
class FormsTests(TestCase):
|
|
||||||
# ForeignObjects should not have any form fields, currently the user needs
|
|
||||||
# to manually deal with the foreignobject relation.
|
|
||||||
class ArticleForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Article
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
def test_foreign_object_form(self):
|
|
||||||
# A very crude test checking that the non-concrete fields do not get form fields.
|
|
||||||
form = FormsTests.ArticleForm()
|
|
||||||
self.assertIn('id_pub_date', form.as_table())
|
|
||||||
self.assertNotIn('active_translation', form.as_table())
|
|
||||||
form = FormsTests.ArticleForm(data={'pub_date': str(datetime.date.today())})
|
|
||||||
self.assertTrue(form.is_valid())
|
|
||||||
a = form.save()
|
|
||||||
self.assertEqual(a.pub_date, datetime.date.today())
|
|
||||||
form = FormsTests.ArticleForm(instance=a, data={'pub_date': '2013-01-01'})
|
|
||||||
a2 = form.save()
|
|
||||||
self.assertEqual(a.pk, a2.pk)
|
|
||||||
self.assertEqual(a2.pub_date, datetime.date(2013, 1, 1))
|
|
||||||
|
|
Loading…
Reference in New Issue