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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
|
@ -112,89 +109,3 @@ class Friendship(models.Model):
|
|||
related_name='to_friend',
|
||||
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
|
||||
from operator import attrgetter
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import FieldError
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
from django.utils import translation
|
||||
|
@ -392,26 +391,3 @@ class MultiColumnFKTests(TestCase):
|
|||
""" See: https://code.djangoproject.com/ticket/21566 """
|
||||
objs = [Person(name="abcd_%s" % i, person_country=self.usa) for i in range(0, 5)]
|
||||
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