1
0
mirror of https://github.com/django/django.git synced 2025-07-06 10:49:17 +00:00

queryset-refactor: Fixed up OneToOneFields (mostly).

They now share as much code as possible with ForeignKeys, but behave more or
less as they did before (the backwards incompatible change is that they are no
longer automatically primary keys -- so more than one per model is permitted).

The documentation still uses an example that is better suited to model
inheritance, but that will change in due course. Also, the admin interface
still shows them as read-only fields, which is probably wrong now, but that can
change on newforms-admin after this branch is merged into trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7096 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-02-08 09:49:17 +00:00
parent 55cd025670
commit accc20d799
5 changed files with 131 additions and 169 deletions

View File

@ -459,13 +459,69 @@ class ReverseManyRelatedObjectsDescriptor(object):
manager.clear() manager.clear()
manager.add(*value) manager.add(*value)
class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
try:
to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
self.to, self.field_name = to, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
self.multiple = True
def get_related_field(self):
"""
Returns the Field in the 'to' object to which this relationship is
tied.
"""
return self.to._meta.get_field_by_name(self.field_name, True)[0]
class OneToOneRel(ManyToOneRel):
def __init__(self, to, field_name, num_in_admin=0, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=None, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False):
# NOTE: *_num_in_admin and num_extra_on_change are intentionally
# ignored here. We accept them as parameters only to match the calling
# signature of ManyToOneRel.__init__().
super(OneToOneRel, self).__init__(to, field_name, num_in_admin,
edit_inline, related_name, limit_choices_to, lookup_overrides,
raw_id_admin)
self.multiple = False
class ManyToManyRel(object):
def __init__(self, to, num_in_admin=0, related_name=None,
filter_interface=None, limit_choices_to=None, raw_id_admin=False, symmetrical=True):
self.to = to
self.num_in_admin = num_in_admin
self.related_name = related_name
self.filter_interface = filter_interface
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
self.edit_inline = False
self.raw_id_admin = raw_id_admin
self.symmetrical = symmetrical
self.multiple = True
assert not (self.raw_id_admin and self.filter_interface), "ManyToManyRels may not use both raw_id_admin and filter_interface"
class ForeignKey(RelatedField, Field): class ForeignKey(RelatedField, Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, to, to_field=None, **kwargs): def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
try: try:
to_name = to._meta.object_name.lower() to_name = to._meta.object_name.lower()
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance(to, basestring), "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT) assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
else: else:
to_field = to_field or to._meta.pk.name to_field = to_field or to._meta.pk.name
kwargs['verbose_name'] = kwargs.get('verbose_name', '') kwargs['verbose_name'] = kwargs.get('verbose_name', '')
@ -475,7 +531,7 @@ class ForeignKey(RelatedField, Field):
warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.") warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
kwargs['edit_inline'] = kwargs.pop('edit_inline_type') kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
kwargs['rel'] = ManyToOneRel(to, to_field, kwargs['rel'] = rel_class(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 3), num_in_admin=kwargs.pop('num_in_admin', 3),
min_num_in_admin=kwargs.pop('min_num_in_admin', None), min_num_in_admin=kwargs.pop('min_num_in_admin', None),
max_num_in_admin=kwargs.pop('max_num_in_admin', None), max_num_in_admin=kwargs.pop('max_num_in_admin', None),
@ -563,82 +619,25 @@ class ForeignKey(RelatedField, Field):
return IntegerField().db_type() return IntegerField().db_type()
return rel_field.db_type() return rel_field.db_type()
class OneToOneField(RelatedField, IntegerField): class OneToOneField(ForeignKey):
"""
A OneToOneField is essentially the same as a ForeignKey, with the exception
that always carries a "unique" constraint with it and the reverse relation
always returns the object pointed to (since there will only ever be one),
rather than returning a list.
"""
def __init__(self, to, to_field=None, **kwargs): def __init__(self, to, to_field=None, **kwargs):
try: kwargs['unique'] = True
to_name = to._meta.object_name.lower() if 'num_in_admin' not in kwargs:
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT kwargs['num_in_admin'] = 0
assert isinstance(to, basestring), "OneToOneField(%r) is invalid. First parameter to OneToOneField must be either a model, a model name, or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT) super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
else:
to_field = to_field or to._meta.pk.name
kwargs['verbose_name'] = kwargs.get('verbose_name', '')
if 'edit_inline_type' in kwargs:
import warnings
warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
kwargs['rel'] = OneToOneRel(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 0),
edit_inline=kwargs.pop('edit_inline', False),
related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
lookup_overrides=kwargs.pop('lookup_overrides', None),
raw_id_admin=kwargs.pop('raw_id_admin', False))
kwargs['primary_key'] = True
IntegerField.__init__(self, **kwargs)
self.db_index = True
def get_attname(self):
return '%s_id' % self.name
def get_validator_unique_lookup_type(self):
return '%s__%s__exact' % (self.name, self.rel.get_related_field().name)
# TODO: Copied from ForeignKey... putting this in RelatedField adversely affects
# ManyToManyField. This works for now.
def prepare_field_objs_and_params(self, manipulator, name_prefix):
params = {'validator_list': self.validator_list[:], 'member_name': name_prefix + self.attname}
if self.rel.raw_id_admin:
field_objs = self.get_manipulator_field_objs()
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
else:
if self.radio_admin:
field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin)
else:
if self.null:
field_objs = [oldforms.NullSelectField]
else:
field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default()
return field_objs, params
def contribute_to_class(self, cls, name):
super(OneToOneField, self).contribute_to_class(cls, name)
setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self))
def contribute_to_related_class(self, cls, related): def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(), SingleRelatedObjectDescriptor(related)) setattr(cls, related.get_accessor_name(),
SingleRelatedObjectDescriptor(related))
if not cls._meta.one_to_one_field: if not cls._meta.one_to_one_field:
cls._meta.one_to_one_field = self cls._meta.one_to_one_field = self
def formfield(self, **kwargs):
defaults = {'form_class': forms.ModelChoiceField, 'queryset': self.rel.to._default_manager.all()}
defaults.update(kwargs)
return super(OneToOneField, self).formfield(**defaults)
def db_type(self):
# The database column type of a OneToOneField is the column type
# of the field to which it points. An exception is if the OneToOneField
# points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField,
# in which case the column type is simply that of an IntegerField.
rel_field = self.rel.get_related_field()
if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)):
return IntegerField().db_type()
return rel_field.db_type()
class ManyToManyField(RelatedField, Field): class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs): def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', None) kwargs['verbose_name'] = kwargs.get('verbose_name', None)
@ -770,59 +769,3 @@ class ManyToManyField(RelatedField, Field):
# so return None. # so return None.
return None return None
class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
try:
to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
self.to, self.field_name = to, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
self.multiple = True
def get_related_field(self):
"""
Returns the Field in the 'to' object to which this relationship is
tied.
"""
return self.to._meta.get_field_by_name(self.field_name, True)[0]
class OneToOneRel(ManyToOneRel):
def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False):
self.to, self.field_name = to, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.related_name = related_name
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
self.multiple = False
class ManyToManyRel(object):
def __init__(self, to, num_in_admin=0, related_name=None,
filter_interface=None, limit_choices_to=None, raw_id_admin=False, symmetrical=True):
self.to = to
self.num_in_admin = num_in_admin
self.related_name = related_name
self.filter_interface = filter_interface
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
self.edit_inline = False
self.raw_id_admin = raw_id_admin
self.symmetrical = symmetrical
self.multiple = True
assert not (self.raw_id_admin and self.filter_interface), "ManyToManyRels may not use both raw_id_admin and filter_interface"

View File

@ -979,9 +979,6 @@ the relationship should work. All are optional:
One-to-one relationships One-to-one relationships
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
The semantics of one-to-one relationships will be changing soon, so we don't
recommend you use them. If that doesn't scare you away, keep reading.
To define a one-to-one relationship, use ``OneToOneField``. You use it just To define a one-to-one relationship, use ``OneToOneField``. You use it just
like any other ``Field`` type: by including it as a class attribute of your like any other ``Field`` type: by including it as a class attribute of your
model. model.
@ -1003,9 +1000,11 @@ As with ``ForeignKey``, a relationship to self can be defined by using the
string ``"self"`` instead of the model name; references to as-yet undefined string ``"self"`` instead of the model name; references to as-yet undefined
models can be made by using a string containing the model name. models can be made by using a string containing the model name.
This ``OneToOneField`` will actually replace the primary key ``id`` field **New in Django development version:** ``OneToOneField`` classes used to
(since one-to-one relations share the same primary key), and will be displayed automatically become the primary key on a model. This is no longer true,
as a read-only field when you edit an object in the admin interface: although you can manually pass in the ``primary_key`` attribute if you like.
Thus, it's now possible to have multilpe fields of type ``OneToOneField`` on a
single model.
See the `One-to-one relationship model example`_ for a full example. See the `One-to-one relationship model example`_ for a full example.

View File

@ -16,7 +16,7 @@ class Place(models.Model):
return u"%s the place" % self.name return u"%s the place" % self.name
class Restaurant(models.Model): class Restaurant(models.Model):
place = models.OneToOneField(Place) place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField() serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField() serves_pizza = models.BooleanField()
@ -38,6 +38,14 @@ class RelatedModel(models.Model):
link = models.OneToOneField(ManualPrimaryKey) link = models.OneToOneField(ManualPrimaryKey)
name = models.CharField(max_length = 50) name = models.CharField(max_length = 50)
class MultiModel(models.Model):
link1 = models.OneToOneField(Place)
link2 = models.OneToOneField(ManualPrimaryKey)
name = models.CharField(max_length=50)
def __unicode__(self):
return u"Multimodel %s" % self.name
__test__ = {'API_TESTS':""" __test__ = {'API_TESTS':"""
# Create a couple of Places. # Create a couple of Places.
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton') >>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
@ -63,8 +71,8 @@ Traceback (most recent call last):
... ...
DoesNotExist: Restaurant matching query does not exist. DoesNotExist: Restaurant matching query does not exist.
# Set the place using assignment notation. Because place is the primary key on Restaurant, # Set the place using assignment notation. Because place is the primary key on
# the save will create a new restaurant # Restaurant, the save will create a new restaurant
>>> r.place = p2 >>> r.place = p2
>>> r.save() >>> r.save()
>>> p2.restaurant >>> p2.restaurant
@ -72,9 +80,9 @@ DoesNotExist: Restaurant matching query does not exist.
>>> r.place >>> r.place
<Place: Ace Hardware the place> <Place: Ace Hardware the place>
# Set the place back again, using assignment in the reverse direction # Set the place back again, using assignment in the reverse direction.
# Need to reget restaurant object first, because the reverse set # Need to reget restaurant object first, because the reverse set can't update
# can't update the existing restaurant instance # the existing restaurant instance
>>> p1.restaurant = r >>> p1.restaurant = r
>>> r.save() >>> r.save()
>>> p1.restaurant >>> p1.restaurant
@ -86,8 +94,7 @@ DoesNotExist: Restaurant matching query does not exist.
# Restaurant.objects.all() just returns the Restaurants, not the Places. # Restaurant.objects.all() just returns the Restaurants, not the Places.
# Note that there are two restaurants - Ace Hardware the Restaurant was created # Note that there are two restaurants - Ace Hardware the Restaurant was created
# in the call to r.place = p2. This means there are multiple restaurants referencing # in the call to r.place = p2.
# a single place...
>>> Restaurant.objects.all() >>> Restaurant.objects.all()
[<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>] [<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>]
@ -165,4 +172,17 @@ DoesNotExist: Restaurant matching query does not exist.
>>> o1.save() >>> o1.save()
>>> o2 = RelatedModel(link=o1, name="secondary") >>> o2 = RelatedModel(link=o1, name="secondary")
>>> o2.save() >>> o2.save()
# You can have multiple one-to-one fields on a model, too.
>>> x1 = MultiModel(link1=p1, link2=o1, name="x1")
>>> x1.save()
>>> o1.multimodel
<MultiModel: Multimodel x1>
# This will fail because each one-to-one field must be unique (and link2=o1 was
# used for x1, above).
>>> MultiModel(link1=p2, link2=o1, name="x1").save()
Traceback (most recent call last):
...
IntegrityError: ...
"""} """}

View File

@ -22,7 +22,7 @@ class Author(models.Model):
class Meta: class Meta:
ordering = ('name',) ordering = ('name',)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -39,21 +39,21 @@ class Article(models.Model):
return self.headline return self.headline
class AuthorProfile(models.Model): class AuthorProfile(models.Model):
author = models.OneToOneField(Author) author = models.OneToOneField(Author, primary_key=True)
date_of_birth = models.DateField() date_of_birth = models.DateField()
def __unicode__(self): def __unicode__(self):
return u"Profile of %s" % self.author return u"Profile of %s" % self.author
class Actor(models.Model): class Actor(models.Model):
name = models.CharField(max_length=20, primary_key=True) name = models.CharField(max_length=20, primary_key=True)
class Meta: class Meta:
ordering = ('name',) ordering = ('name',)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Movie(models.Model): class Movie(models.Model):
actor = models.ForeignKey(Actor) actor = models.ForeignKey(Actor)
title = models.CharField(max_length=50) title = models.CharField(max_length=50)
@ -63,7 +63,7 @@ class Movie(models.Model):
def __unicode__(self): def __unicode__(self):
return self.title return self.title
class Score(models.Model): class Score(models.Model):
score = models.FloatField() score = models.FloatField()
@ -100,7 +100,7 @@ __test__ = {'API_TESTS':"""
>>> dom = minidom.parseString(xml) >>> dom = minidom.parseString(xml)
# Deserializing has a similar interface, except that special DeserializedObject # Deserializing has a similar interface, except that special DeserializedObject
# instances are returned. This is because data might have changed in the # instances are returned. This is because data might have changed in the
# database since the data was serialized (we'll simulate that below). # database since the data was serialized (we'll simulate that below).
>>> for obj in serializers.deserialize("xml", xml): >>> for obj in serializers.deserialize("xml", xml):
... print obj ... print obj
@ -148,7 +148,7 @@ __test__ = {'API_TESTS':"""
>>> Article.objects.all() >>> Article.objects.all()
[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>] [<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
# If you use your own primary key field (such as a OneToOneField), # If you use your own primary key field (such as a OneToOneField),
# it doesn't appear in the serialized field list - it replaces the # it doesn't appear in the serialized field list - it replaces the
# pk identifier. # pk identifier.
>>> profile = AuthorProfile(author=joe, date_of_birth=datetime(1970,1,1)) >>> profile = AuthorProfile(author=joe, date_of_birth=datetime(1970,1,1))
@ -186,7 +186,7 @@ __test__ = {'API_TESTS':"""
>>> print serializers.serialize("json", Article.objects.all(), fields=('headline','pub_date')) >>> print serializers.serialize("json", Article.objects.all(), fields=('headline','pub_date'))
[{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:11"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}] [{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:11"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}]
# Every string is serialized as a unicode object, also primary key # Every string is serialized as a unicode object, also primary key
# which is 'varchar' # which is 'varchar'
>>> ac = Actor(name="Zażółć") >>> ac = Actor(name="Zażółć")
>>> mv = Movie(title="Gęślą jaźń", actor=ac) >>> mv = Movie(title="Gęślą jaźń", actor=ac)
@ -247,12 +247,12 @@ try:
pk: 2 pk: 2
<BLANKLINE> <BLANKLINE>
>>> obs = list(serializers.deserialize("yaml", serialized)) >>> obs = list(serializers.deserialize("yaml", serialized))
>>> for i in obs: >>> for i in obs:
... print i ... print i
<DeserializedObject: Just kidding; I love TV poker> <DeserializedObject: Just kidding; I love TV poker>
<DeserializedObject: Time to reform copyright> <DeserializedObject: Time to reform copyright>
""" """
except ImportError: pass except ImportError: pass

View File

@ -77,7 +77,7 @@ class USStateData(models.Model):
class XMLData(models.Model): class XMLData(models.Model):
data = models.XMLField(null=True) data = models.XMLField(null=True)
class Tag(models.Model): class Tag(models.Model):
"""A tag on an item.""" """A tag on an item."""
data = models.SlugField() data = models.SlugField()
@ -93,36 +93,36 @@ class GenericData(models.Model):
data = models.CharField(max_length=30) data = models.CharField(max_length=30)
tags = generic.GenericRelation(Tag) tags = generic.GenericRelation(Tag)
# The following test classes are all for validation # The following test classes are all for validation
# of related objects; in particular, forward, backward, # of related objects; in particular, forward, backward,
# and self references. # and self references.
class Anchor(models.Model): class Anchor(models.Model):
"""This is a model that can be used as """This is a model that can be used as
something for other models to point at""" something for other models to point at"""
data = models.CharField(max_length=30) data = models.CharField(max_length=30)
class UniqueAnchor(models.Model): class UniqueAnchor(models.Model):
"""This is a model that can be used as """This is a model that can be used as
something for other models to point at""" something for other models to point at"""
data = models.CharField(unique=True, max_length=30) data = models.CharField(unique=True, max_length=30)
class FKData(models.Model): class FKData(models.Model):
data = models.ForeignKey(Anchor, null=True) data = models.ForeignKey(Anchor, null=True)
class M2MData(models.Model): class M2MData(models.Model):
data = models.ManyToManyField(Anchor, null=True) data = models.ManyToManyField(Anchor, null=True)
class O2OData(models.Model): class O2OData(models.Model):
# One to one field can't be null, since it is a PK. # One to one field can't be null here, since it is a PK.
data = models.OneToOneField(Anchor) data = models.OneToOneField(Anchor, primary_key=True)
class FKSelfData(models.Model): class FKSelfData(models.Model):
data = models.ForeignKey('self', null=True) data = models.ForeignKey('self', null=True)
class M2MSelfData(models.Model): class M2MSelfData(models.Model):
data = models.ManyToManyField('self', null=True, symmetrical=False) data = models.ManyToManyField('self', null=True, symmetrical=False)
@ -142,7 +142,7 @@ class FKDataToO2O(models.Model):
class BooleanPKData(models.Model): class BooleanPKData(models.Model):
data = models.BooleanField(primary_key=True) data = models.BooleanField(primary_key=True)
class CharPKData(models.Model): class CharPKData(models.Model):
data = models.CharField(max_length=30, primary_key=True) data = models.CharField(max_length=30, primary_key=True)