From 62b813c17d9be1cfb8e60ca1107b98a6604decd5 Mon Sep 17 00:00:00 2001 From: Alejandro Blanco Date: Tue, 3 Jan 2017 15:28:39 +0100 Subject: [PATCH] Make SEO objects depend on the site too If you have several sites in your Django portal, then you'll have one SEO metadata object per path, language and site combination --- painlessseo/admin.py | 15 ++-- painlessseo/locale/es/LC_MESSAGES/django.po | 4 + painlessseo/models.py | 83 ++++++++++++------- .../templates/painlessseo/metadata.html | 3 +- painlessseo/templatetags/seo.py | 5 +- 5 files changed, 71 insertions(+), 39 deletions(-) diff --git a/painlessseo/admin.py b/painlessseo/admin.py index b02470c..110cb99 100644 --- a/painlessseo/admin.py +++ b/painlessseo/admin.py @@ -41,7 +41,8 @@ def clean(self): obj = cleaned_data.get('id') for lang, fields in settings.SEO_ADMIN_FORM_REQUIRED_FIELDS.items(): if obj.lang_code == lang: - cleaned_data = self.validate_required_fields(cleaned_data, fields) + cleaned_data = self.validate_required_fields(cleaned_data, + fields) return cleaned_data @@ -51,8 +52,8 @@ class SeoMetadataInline(GenericStackedInline): max_num = 0 model = SeoMetadata - fields = ('language', 'title', 'description') - readonly_fields = ('language', ) + fields = ('language', 'site', 'title', 'description') + readonly_fields = ('language', 'site', ) def has_delete_permission(self, request, obj=None): return False @@ -67,13 +68,13 @@ class SeoMetadataWithValidationInline(SeoMetadataInline): class SeoMetadataAdmin(admin.ModelAdmin): - list_display = ('path', 'lang_code', ) + list_display = ('path', 'lang_code', 'site', ) search_fields = ['path', ] - list_filter = ('lang_code', ) + list_filter = ('lang_code', 'site', ) exclude = ('content_type', 'object_id', ) - fields = ('language', 'title', 'description') - readonly_fields = ('language', ) + fields = ('language', 'site', 'title', 'description', ) + readonly_fields = ('language', 'site', ) def language(self, obj): return get_language(obj) diff --git a/painlessseo/locale/es/LC_MESSAGES/django.po b/painlessseo/locale/es/LC_MESSAGES/django.po index 4ead2bf..994ce6e 100644 --- a/painlessseo/locale/es/LC_MESSAGES/django.po +++ b/painlessseo/locale/es/LC_MESSAGES/django.po @@ -44,3 +44,7 @@ msgstr "DescripciĆ³n" #: models.py:24 models.py:25 msgid "SEO metadata" msgstr "SEO metadatos" + +#: models.py:45 +msgid "Site" +msgstr "Sitio" diff --git a/painlessseo/models.py b/painlessseo/models.py index ddd0484..37df429 100644 --- a/painlessseo/models.py +++ b/painlessseo/models.py @@ -6,7 +6,8 @@ except: from django.contrib.contenttypes.generic import GenericForeignKey -from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.models import ContentType +from django.contrib.sites.models import Site from django.core.exceptions import ImproperlyConfigured from django.db import models from django.utils.translation import activate, get_language @@ -16,23 +17,40 @@ class SeoMetadata(models.Model): - content_type = models.ForeignKey(ContentType, null=True, blank=True) - object_id = models.PositiveIntegerField(null=True, blank=True) - content_object = GenericForeignKey('content_type', 'object_id') - path = models.CharField(verbose_name=_('Path'), max_length=200, db_index=True, - help_text=_("This should be an absolute path, excluding the domain name. Example: '/foo/bar/'.")) - lang_code = models.CharField(verbose_name=_('Language'), max_length=2, - choices=settings.SEO_LANGUAGES, - default=settings.DEFAULT_LANG_CODE) - title = models.CharField(verbose_name=_('Title'), max_length=68, blank=True) - description = models.CharField(verbose_name=_('Description'), max_length=155, blank=True) + content_type = models.ForeignKey( + ContentType, null=True, blank=True) + + object_id = models.PositiveIntegerField( + null=True, blank=True) + + content_object = GenericForeignKey( + 'content_type', 'object_id') + + path = models.CharField( + verbose_name=_('Path'), max_length=200, db_index=True, + help_text=_('This should be an absolute path, excluding the domain ' + 'name. Example: \'/foo/bar/\'.')) + + lang_code = models.CharField( + verbose_name=_('Language'), max_length=2, + choices=settings.SEO_LANGUAGES, default=settings.DEFAULT_LANG_CODE) + + title = models.CharField( + verbose_name=_('Title'), max_length=68, blank=True) + + description = models.CharField( + verbose_name=_('Description'), max_length=155, blank=True) + + site = models.ForeignKey( + Site, verbose_name=_('Site'), related_name='seo_metadata', null=True, + blank=True, default=None) class Meta: verbose_name = _('SEO metadata') verbose_name_plural = _('SEO metadata') db_table = 'seo_metadata' - unique_together = (('path', 'lang_code'), ) - ordering = ('path', 'lang_code') + unique_together = (('path', 'lang_code', 'site'), ) + ordering = ('path', 'lang_code', 'site') def __unicode__(self): return "Language: %s | URL: %s" % (self.lang_code, self.path) @@ -40,33 +58,38 @@ def __unicode__(self): def update_seo(sender, instance, **kwargs): active_lang = get_language() + sites = Site.objects.all() for lang_code, lang_name in settings.SEO_LANGUAGES: activate(lang_code) - try: - sm = SeoMetadata.objects.get( - content_type=ContentType.objects.get_for_model(instance), - object_id=instance.id, lang_code=lang_code) - if instance.get_absolute_url() != sm.path: - sm.path = instance.get_absolute_url() - except SeoMetadata.DoesNotExist: - sm = SeoMetadata( - lang_code=lang_code, content_object=instance, - path=instance.get_absolute_url()) - sm.save() + for site in sites: + try: + sm = SeoMetadata.objects.get( + content_type=ContentType.objects.get_for_model(instance), + object_id=instance.id, lang_code=lang_code, site=site) + if instance.get_absolute_url() != sm.path: + sm.path = instance.get_absolute_url() + except SeoMetadata.DoesNotExist: + sm = SeoMetadata( + lang_code=lang_code, content_object=instance, + path=instance.get_absolute_url(), site=site) + sm.save() activate(active_lang) def delete_seo(sender, instance, **kwargs): ctype = ContentType.objects.get_for_model(instance) - for sm in SeoMetadata.objects.filter(content_type=ctype, object_id=instance.id): - sm.delete() + SeoMetadata.objects.filter( + content_type=ctype, object_id=instance.id).delete() def register_seo_signals(): for app, model in settings.SEO_MODELS: ctype = ContentType.objects.get(app_label=app, model=model) if not hasattr(ctype.model_class(), 'get_absolute_url'): - raise ImproperlyConfigured("Needed 'get_absolute_url' method not " - "defined on '%s.%s' model." % (app, model)) - models.signals.post_save.connect(update_seo, sender=ctype.model_class(), weak=False) - models.signals.pre_delete.connect(delete_seo, sender=ctype.model_class(), weak=False) + raise ImproperlyConfigured( + 'Needed \'get_absolute_url\' method not defined on \'%s.%s\' ' + 'model.' % (app, model)) + models.signals.post_save.connect( + update_seo, sender=ctype.model_class(), weak=False) + models.signals.pre_delete.connect( + delete_seo, sender=ctype.model_class(), weak=False) diff --git a/painlessseo/templates/painlessseo/metadata.html b/painlessseo/templates/painlessseo/metadata.html index 62d2239..4ca6566 100644 --- a/painlessseo/templates/painlessseo/metadata.html +++ b/painlessseo/templates/painlessseo/metadata.html @@ -1,5 +1,6 @@ {% load seo %} + {{title}} {% with description|single_quotes as safer_description %} - + {% endwith %} diff --git a/painlessseo/templatetags/seo.py b/painlessseo/templatetags/seo.py index ea080c6..820213d 100644 --- a/painlessseo/templatetags/seo.py +++ b/painlessseo/templatetags/seo.py @@ -1,6 +1,7 @@ # Copyright (C) 2014 Glamping Hub (https://glampinghub.com) # License: BSD 3-Clause +from django.contrib.sites.models import Site from django.forms.models import model_to_dict from django.template import Library from django.utils.translation import get_language @@ -24,9 +25,11 @@ def get_seo(context, **kwargs): request = context['request'] path = request.path lang_code = get_language()[:2] + site = Site.objects.get_current() try: metadata = model_to_dict(SeoMetadata.objects.get(path=path, - lang_code=lang_code)) + lang_code=lang_code, + site=site)) except SeoMetadata.DoesNotExist: metadata = {} result = {}