mirror of
https://github.com/jazzband/django-ddp.git
synced 2026-04-24 00:44:44 +00:00
Add models and various shortcuts to allow for AleaIdField with primary_key=True.
This commit is contained in:
parent
cb46caf9ff
commit
43b35a12ce
3 changed files with 105 additions and 14 deletions
|
|
@ -104,7 +104,7 @@ class SubscriptionCollection(admin.ModelAdmin):
|
|||
|
||||
|
||||
for name, attr in vars(models).items():
|
||||
if hasattr(attr, '_meta'):
|
||||
if hasattr(attr, '_meta') and not getattr(attr._meta, 'abstract'):
|
||||
model_admin = locals().get(name, None)
|
||||
if model_admin is not False:
|
||||
admin.site.register(attr, model_admin)
|
||||
|
|
|
|||
22
dddp/api.py
22
dddp/api.py
|
|
@ -28,7 +28,9 @@ import ejson
|
|||
from dddp import (
|
||||
AlreadyRegistered, THREAD_LOCAL as this, ADDED, CHANGED, REMOVED,
|
||||
)
|
||||
from dddp.models import Connection, Subscription, get_meteor_id, get_meteor_ids
|
||||
from dddp.models import (
|
||||
AleaIdField, Connection, Subscription, get_meteor_id, get_meteor_ids,
|
||||
)
|
||||
|
||||
|
||||
XMIN = {'select': {'xmin': "'xmin'"}}
|
||||
|
|
@ -600,9 +602,12 @@ class DDP(APIMixin):
|
|||
model_name=model_name(qs.model),
|
||||
collection_name=col.name,
|
||||
)
|
||||
meteor_ids = get_meteor_ids(
|
||||
qs.model, qs.values_list('pk', flat=True),
|
||||
)
|
||||
if isinstance(col.model._meta.pk, AleaIdField):
|
||||
meteor_ids = None
|
||||
else:
|
||||
meteor_ids = get_meteor_ids(
|
||||
qs.model, qs.values_list('pk', flat=True),
|
||||
)
|
||||
for obj in qs:
|
||||
payload = col.obj_change_as_msg(obj, ADDED, meteor_ids)
|
||||
this.send(payload)
|
||||
|
|
@ -615,9 +620,12 @@ class DDP(APIMixin):
|
|||
connection=this.ws.connection, sub_id=id_,
|
||||
)
|
||||
for col, qs in self.sub_unique_objects(sub):
|
||||
meteor_ids = get_meteor_ids(
|
||||
qs.model, qs.values_list('pk', flat=True),
|
||||
)
|
||||
if isinstance(col.model._meta.pk, AleaIdField):
|
||||
meteor_ids = None
|
||||
else:
|
||||
meteor_ids = get_meteor_ids(
|
||||
qs.model, qs.values_list('pk', flat=True),
|
||||
)
|
||||
for obj in qs:
|
||||
payload = col.obj_change_as_msg(obj, REMOVED, meteor_ids)
|
||||
this.send(payload)
|
||||
|
|
|
|||
|
|
@ -23,10 +23,29 @@ def get_meteor_id(obj_or_model, obj_pk=None):
|
|||
if model is ObjectMapping:
|
||||
# this doesn't make sense - raise TypeError
|
||||
raise TypeError("Can't map ObjectMapping instances through self.")
|
||||
if obj_or_model is not model and obj_pk is None:
|
||||
obj_pk = str(obj_or_model.pk)
|
||||
|
||||
# try getting value of AleaIdField straight from instance if possible
|
||||
if isinstance(obj_or_model, model):
|
||||
# obj_or_model is an instance, not a model.
|
||||
if isinstance(meta.pk, AleaIdField):
|
||||
return obj_or_model.pk
|
||||
alea_unique_fields = [
|
||||
field
|
||||
for field in meta.local_fields
|
||||
if isinstance(field, AleaIdField) and field.unique
|
||||
]
|
||||
if len(alea_unique_fields) == 1:
|
||||
# found an AleaIdField with unique=True, assume it's got the value.
|
||||
return getattr(obj_or_model, alea_unique_fields[0].attname)
|
||||
if obj_pk is None:
|
||||
# fall back to primary key, but coerce as string type for lookup.
|
||||
obj_pk = str(obj_or_model.pk)
|
||||
|
||||
if obj_pk is None:
|
||||
# bail out if args are (model, pk) but pk is None.
|
||||
return None
|
||||
|
||||
# fallback to using AleaIdField from ObjectMapping model.
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
try:
|
||||
return ObjectMapping.objects.values_list(
|
||||
|
|
@ -47,6 +66,13 @@ def get_meteor_id(obj_or_model, obj_pk=None):
|
|||
def get_meteor_ids(model, object_ids):
|
||||
"""Return Alea ID mapping for all given ids of specified model."""
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
# Django model._meta is now public API -> pylint: disable=W0212
|
||||
meta = model._meta
|
||||
if isinstance(meta.pk, AleaIdField):
|
||||
# primary_key is an AleaIdField, use it.
|
||||
return collections.OrderedDict(
|
||||
(obj_pk, obj_pk) for obj_pk in object_ids
|
||||
)
|
||||
result = collections.OrderedDict(
|
||||
(str(obj_pk), None)
|
||||
for obj_pk
|
||||
|
|
@ -59,11 +85,10 @@ def get_meteor_ids(model, object_ids):
|
|||
result[obj_pk] = meteor_id
|
||||
for obj_pk, meteor_id in result.items():
|
||||
if meteor_id is None:
|
||||
# Django model._meta is now public API -> pylint: disable=W0212
|
||||
result[obj_pk] = ObjectMapping.objects.create(
|
||||
content_type=content_type,
|
||||
object_id=obj_pk,
|
||||
meteor_id=meteor_random_id('/collection/%s' % model._meta),
|
||||
meteor_id=meteor_random_id('/collection/%s' % meta),
|
||||
).meteor_id
|
||||
return result
|
||||
|
||||
|
|
@ -71,10 +96,32 @@ def get_meteor_ids(model, object_ids):
|
|||
@transaction.atomic
|
||||
def get_object_id(model, meteor_id):
|
||||
"""Return an object ID for the given meteor_id."""
|
||||
if meteor_id is None:
|
||||
return None
|
||||
# Django model._meta is now public API -> pylint: disable=W0212
|
||||
meta = model._meta
|
||||
|
||||
if model is ObjectMapping:
|
||||
# this doesn't make sense - raise TypeError
|
||||
raise TypeError("Can't map ObjectMapping instances through self.")
|
||||
# Django model._meta is now public API -> pylint: disable=W0212
|
||||
|
||||
if isinstance(meta.pk, AleaIdField):
|
||||
# meteor_id is the primary key
|
||||
return meteor_id
|
||||
|
||||
alea_unique_fields = [
|
||||
field
|
||||
for field in meta.local_fields
|
||||
if isinstance(field, AleaIdField) and field.unique
|
||||
]
|
||||
if len(alea_unique_fields) == 1:
|
||||
# found an AleaIdField with unique=True, assume it's got the value.
|
||||
return model.objects.values_list(
|
||||
'pk', flat=True,
|
||||
).get(**{
|
||||
alea_unique_fields[0].attname: meteor_id,
|
||||
})
|
||||
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
return ObjectMapping.objects.filter(
|
||||
content_type=content_type,
|
||||
|
|
@ -85,6 +132,12 @@ def get_object_id(model, meteor_id):
|
|||
@transaction.atomic
|
||||
def get_object(model, meteor_id, *args, **kwargs):
|
||||
"""Return an object for the given meteor_id."""
|
||||
# Django model._meta is now public API -> pylint: disable=W0212
|
||||
meta = model._meta
|
||||
if isinstance(meta.pk, AleaIdField):
|
||||
# meteor_id is the primary key
|
||||
return model.objects.filter(*args, **kwargs).get(pk=meteor_id)
|
||||
|
||||
return model.objects.filter(*args, **kwargs).get(
|
||||
pk=get_object_id(model, meteor_id),
|
||||
)
|
||||
|
|
@ -97,11 +150,41 @@ class AleaIdField(models.CharField):
|
|||
def __init__(self, *args, **kwargs):
|
||||
"""Assume max_length of 17 to match Meteor implementation."""
|
||||
kwargs.update(
|
||||
default=meteor_random_id,
|
||||
editable=False,
|
||||
max_length=17,
|
||||
)
|
||||
super(AleaIdField, self).__init__(*args, **kwargs)
|
||||
|
||||
def deconstruct(self):
|
||||
"""Return details on how this field was defined."""
|
||||
name, path, args, kwargs = super(AleaIdField, self).deconstruct()
|
||||
del kwargs['max_length']
|
||||
return name, path, args, kwargs
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
"""Generate ID if required."""
|
||||
_, _, _, kwargs = self.deconstruct()
|
||||
val = getattr(model_instance, self.attname)
|
||||
if val is None and kwargs.get('default', None) is None:
|
||||
val = meteor_random_id('/collection/%s' % model_instance._meta)
|
||||
setattr(model_instance, self.attname, val)
|
||||
return val
|
||||
|
||||
|
||||
class AleaIdMixin(models.Model):
|
||||
|
||||
"""Django model mixin that provides AleaIdField field (as _id)."""
|
||||
|
||||
id = AleaIdField(
|
||||
primary_key=True,
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
|
||||
"""Model meta options for AleaIdMixin."""
|
||||
|
||||
abstract = True
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ObjectMapping(models.Model):
|
||||
|
|
|
|||
Loading…
Reference in a new issue