diff --git a/utils/feed_fetcher.py b/utils/feed_fetcher.py index e471bc59c..5c7686cc4 100644 --- a/utils/feed_fetcher.py +++ b/utils/feed_fetcher.py @@ -14,7 +14,6 @@ import isodate import urlparse from django.conf import settings from django.db import IntegrityError -from django.db.models.fields.related_descriptors import RelatedObjectDoesNotExist from django.core.cache import cache from apps.reader.models import UserSubscription from apps.rss_feeds.models import Feed, MStory @@ -470,11 +469,7 @@ class ProcessFeed: if self.feed.feed_title != original_title: self.feed.save(update_fields=['feed_title']) - try: - tagline = self.fpf.feed.get('tagline', self.feed.data.feed_tagline) - except RelatedObjectDoesNotExist: - tagline = None - + tagline = self.fpf.feed.get('tagline', self.feed.data.feed_tagline) if tagline: original_tagline = self.feed.data.feed_tagline self.feed.data.feed_tagline = smart_unicode(tagline) diff --git a/utils/fields.py b/utils/fields.py index 361414f8d..8c9a6fdcc 100644 --- a/utils/fields.py +++ b/utils/fields.py @@ -1,14 +1,41 @@ +import django from django.db.models import OneToOneField -from django.db.models.fields.related import ReverseOneToOneDescriptor +from django.db.transaction import atomic +try: + from django.db.models.fields.related_descriptors import ( + ReverseOneToOneDescriptor, + ) +except ImportError: + from django.db.models.fields.related import SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor + class AutoSingleRelatedObjectDescriptor(ReverseOneToOneDescriptor): + """ + The descriptor that handles the object creation for an AutoOneToOneField. + """ + + @atomic def __get__(self, instance, instance_type=None): + model = getattr(self.related, 'related_model', self.related.model) + try: - return super(AutoSingleRelatedObjectDescriptor, self).__get__(instance, instance_type) - except self.related.model.DoesNotExist: - obj = self.related.model(**{self.related.field.name: instance}) - obj.save() + return ( + super(AutoSingleRelatedObjectDescriptor, self) + .__get__(instance, instance_type) + ) + except model.DoesNotExist: + # Using get_or_create instead() of save() or create() as it better handles race conditions + obj, _ = model.objects.get_or_create(**{self.related.field.name: instance}) + + # Update Django's cache, otherwise first 2 calls to obj.relobj + # will return 2 different in-memory objects + if django.VERSION >= (2, 0): + self.related.set_cached_value(instance, obj) + self.related.field.set_cached_value(obj, instance) + else: + setattr(instance, self.cache_name, obj) + setattr(obj, self.related.field.get_cache_name(), instance) return obj @@ -25,4 +52,4 @@ class AutoOneToOneField(OneToOneField): icq = models.IntegerField(max_length=255, null=True) ''' def contribute_to_related_class(self, cls, related): - setattr(cls, related.get_accessor_name(), AutoSingleRelatedObjectDescriptor(related)) \ No newline at end of file + setattr(cls, related.get_accessor_name(), AutoSingleRelatedObjectDescriptor(related))