-
-
Save rgutierrez-cotech/cf09f015dadd214d6c82b772b7d87be2 to your computer and use it in GitHub Desktop.
Context model full version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Context(models.Model): | |
""" | |
A through model for connecting two models together. Only | |
one field should have a value; the rest should remain null. | |
The models linked in the one-to-one fields here will be the | |
parent, or context, of a single other model instance, which | |
will use a ForeignKey to Context. Any time we add a model | |
that can be a context to another model, we need to add a | |
new field to the model here. | |
This model is used instead of GenericForeignKey; it allows | |
us to specify a `context` ForeignKey on any model, and then | |
define inside Context here which model types can be "contexts". | |
""" | |
activity = models.OneToOneField( | |
'improvement_library.Activity', | |
null=True, blank=True, | |
on_delete=models.CASCADE) | |
post = models.OneToOneField( | |
'improvement_library.Post', | |
null=True, blank=True, | |
on_delete=models.CASCADE) | |
announcement = models.OneToOneField( | |
'improvement_library.Announcement', | |
null=True, blank=True, | |
on_delete=models.CASCADE) | |
group = models.OneToOneField( | |
'improvement_library.Group', | |
null=True, blank=True, | |
on_delete=models.CASCADE) | |
chcomment = models.OneToOneField( | |
'improvement_library.CHComment', | |
null=True, blank=True, | |
on_delete=models.CASCADE) | |
def clean_fields(self, exclude=None): | |
""" | |
Ensure only one field has a value at any time | |
""" | |
# there's probably a better way to do this | |
def _sum_field_has_val(prev, cur): | |
if cur: | |
return prev + 1 | |
else: | |
return prev | |
field_values = [0] + [ getattr(self, field.name, None) \ | |
for field in self._meta.get_fields() ] | |
if reduce(_sum_field_has_val, field_values) > 1: | |
raise ValidationError('Only one field can have a value at a time!') | |
super().clean_fields(exclude=exclude) | |
@classmethod | |
def get_or_create(cls, context_type, context): | |
if isinstance(context, (int, str)): | |
context_args = {} | |
context_args['{}_id'.format(context_type)] = context | |
else: | |
context_args = {context_type: context} | |
try: | |
c = cls.objects.get(**context_args) | |
except Context.DoesNotExist: | |
c = cls(**context_args) | |
c.save() | |
return c | |
def save(self, *args, **kwargs): | |
""" | |
Performs some initial checks and verifications before | |
saving the instance. | |
Clean manually since we are not using a ModelForm to | |
instantiate | |
""" | |
self.full_clean() | |
return super().save(*args, **kwargs) | |
def get_context_field_name(obj): | |
""" | |
A helper function used throughout the "context" interface | |
Get the "fake" context field from an object, either a db | |
model or a dict-like object. | |
""" | |
if isinstance(obj, ContextualModel): | |
dict_like_object = obj.__dict__ | |
else: | |
dict_like_object = obj | |
for field_name, field_val in dict_like_object.items(): | |
if field_name.startswith('context_'): | |
return field_name | |
raise AttributeError("No initial context property found on the class instance or dictionary object. " | |
"Please set a 'context_<obj_name>' property/key on the class/dictionary, where <obj_name> " | |
"is the lowercase name of a model; give it an instance of the model or id of a model " | |
"as a value.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment