Package: django-ok-seo
This app allows you to add meta tags and OpenGraph properties to your HTML responses.InstallationInstall with pip:$pipinstalldjango-ok-seoUpdate INSTALLED_APPS:INSTALLED_APPS=[...'seo','modeltranslation',# optional'django_jinja',# optional for jinja2 global function...]Make migrations$pythonmanage.pymigrateIf you want to makeseo modelstranslatable, you need to installdjango-modeltranslationpackage. After that run:$pythonmanage.pymakemigrations$pythonmanage.pymigrateto create new fields inseo modelsfor each language.FeaturesThere are two options of usage:UrlSeo- a model to fetch seo data by an url.ViewSeoandModelInstanceSeo- models to attach seo data for specific views (using choices) and objects (using “generic” relationships).Available settingsSEO_USE_URL_SEO- Flag to use (display in an admin interface) onlyUrlSeomodel.Falseby default.SEO_VIEWS_CHOICES- Tuple of tuples for using withViewSeo. The first value is the value to use in a code and a second is a verbose (translated) value.For example:SEO_VIEWS_CHOICES=(('index','Index page'),('faq','FAQ page'),)SEO_MODELS- List of models names to limit content type choices for ‘ModelInstanceSeo’.For example (use lowercase):SEO_MODELS=['article.article','auth.user']SEO_DEFAULT_IMAGE- Path to default image, which will be used for ‘og:image’ property.SEO_IMAGE_WIDTH- Value ofwidthfor image.1200by default.SEO_IMAGE_HEIGHT- Value ofheightfor image.630by default.SEO_IMAGE_EXTENSIONS- List of allowed image extensions for ImageField in seo model.SEO_IMAGE_STORAGE- Custom file storage for ImageField in seo model. ‘django.core.files.storage.FileSystemStorage’ by default.SEO_OBJECT_IMAGE_FIELD- A name of field to get image from an object. ‘image’ by default.By default:['jpg','jpeg','png']SEO_OG_TYPES- Tuple of tuples of open graph object types.By default:DEFAULT_OBJECT_TYPES=(('website',pgettext_lazy('OG types','Website')),('article',pgettext_lazy('OG types','Article')))SEO_TWITTER_TYPES- Tuple of tuples of twitter card types.By default:DEFAULT_TWITTER_TYPES=(('summary',pgettext_lazy('Twitter card types','Summary Card')),('summary_large_image',pgettext_lazy('Twitter card types','Summary Card with Large Image')),('player',pgettext_lazy('Twitter card types','Player')),('app',pgettext_lazy('Twitter card types','App')),)SEO_FB_APP_ID- Common Facebook application id. Also, You can set custom id in facebook_app_id field for each seo instance.SEO_HTML_ADMIN_WIDGET- Dictionary with default widget fortop_textandbottom_texttext fields in django admin interface.For example:SEO_HTML_ADMIN_WIDGET={'widget':'TinyMCE','widget_path':'tinymce.widgets'}SEO_DEBUG_MODE- Sets debug mode. IfTrueadds<meta name=”robots” content=”noindex,nofollow”>to all pages.SEO_URL_SEO_SITEMAP_PRIORITY-UrlSeositemap priority.1by default.SEO_URL_SEO_SITEMAP_CHANGEFREQ-UrlSeositemap changefreq.alwaysby default.SEO_VIEWS_CONTEXT_NAME- name of context variable for mixins for class based views.seoby default.Basic example to use:Admin inline (forModelInstanceSeo):# admin.pyfromdjango.contribimportadminfromseo.adminimportModelInstanceSeoInlinefromapps.article.modelsimportArticle@admin.register(Article)classArticleAdmin(admin.ModelAdmin):inlines=[ModelInstanceSeoInline]Views (examples for all models):# views.pyfromdjango.views.genericimportDetailView,TemplateViewfromseo.mixins.viewsimport(ViewSeoMixin,ModelInstanceViewSeoMixin,UrlSeoMixin)fromapps.article.modelsimportArticleclassIndexView(ViewSeoMixin,TemplateView):seo_view='index'template_name='index.html'classIndexViewJinja(ViewSeoMixin,TemplateView):seo_view='index'template_name='jinja/index.jinja'classArticleDetailView(ModelInstanceViewSeoMixin,DetailView):template_name='article.html'model=Articlepk_url_kwarg='id'classArticleDetailViewJinja(ModelInstanceViewSeoMixin,DetailView):template_name='jinja/article.jinja'model=Articlepk_url_kwarg='id'classIndexUrlSeoView(UrlSeoMixin,TemplateView):template_name='index.html'classArticleUrlSeoDetailView(UrlSeoMixin,DetailView):template_name='article.html'model=Articlepk_url_kwarg='id'Context processor (forUrlSeo):# ...'seo.context_processors.seo',Middleware (forUrlSeo) to use fromrequestvariable in tepmlates:MIDDLEWARE=[...# seo'seo.middleware.url_seo_middleware']In templates:{% load seo %}<head><metacharset="UTF-8">{% get_seo_data request.seo %}</head>Your templates:*.html{% load seo %}<head><metacharset="UTF-8">{% get_seo_data seo %}</head><!-- Optional: -->...<h1>{{ seo.h1 }}</h1>...<divid='seo_text'>{{ seo.seo_text|safe }}</div>*.jinja<head>
    <meta charset="UTF-8">{{get_jinja_seo_data(seo)}}</head>
...
<!-- Optional: -->
...
<h1>{{seo.h1}}</h1>
...
<div id='seo_text'>{{seo.seo_text|safe}}</div>InheritanceYou can inherit your models fromSeoTagsMixinand override necessary methods to set custom seo data for your objects.fromdjango.dbimportmodelsfromseo.mixins.modelsimportSeoTagsMixinclassArticle(SeoTagsMixin,models.Model):preview=models.ImageField()short_description=models.TextField(max_length=1000)...defget_meta_description(self)->str:"""
        Return meta description
        """returnself.short_descriptiondefget_meta_image_field(self):"""
        Return image field instance to get image url
        """returnself.previewAnd in a template for your DetailView, you can use:<head><metacharset="UTF-8">{% get_seo_data object %}</head>where object is your defaultcontext_object_name.Also, you can use this way withModelInstanceViewSeoMixinto still useModelInstanceSeo, but get some data from a content object. To reach this goal, you need to override next methods:defget_meta_title(self)->str:"""
    Return meta title
    """return_('{}< Some super title').format(str(self))defget_meta_description(self)->str:"""
    Return meta description
    """return_('{}➤ Wow! ''✔ Amazing! ''❖ Marvelous!').format(str(self))defget_h1_title(self)->str:"""
    Return  h1 title
    """returnstr(self)If you want to get an image from the content object, you may left the image field empty inModelInstanceSeoinstance. If your image field has some specific name, you need to define a property with a nameimage.Sitemap# sitemaps.pyfromseo.sitemapsimportUrlSeoSitemap...sitemaps={'pages':UrlSeoSitemap}# urls.py...fromdjango.contrib.sitemapsimportviewsassitemap_views...fromsomewhere.sitemapsimportsitemapsurlpatterns=[url(r'^sitemap\.xml$',sitemap_views.index,{'sitemaps':sitemaps},name='sitemap'),url(r'^sitemap-(?P<section>\w+)\.xml$',sitemap_views.sitemap,{'sitemaps':sitemaps},name='django.contrib.sitemaps.views.sitemap'),]