Skip to content

Commit

Permalink
WIP make content models swappable, closes stephenmcd#1160
Browse files Browse the repository at this point in the history
(It's marked as WIP because I left some XXX with questions.)

Any of the following models can now be swapped by your own version:

* Page
* RichTextPage
* Link
* BlogPost
* BlogCategory
* Form
* FormEntry
* FieldEntry
* Gallery
* GalleryImage

So you can keep the same features and API but add fields, methods, inherit from
other classes (geo models for geo fields, third-party models...), etc. Just
make sure you inherit from each respective abstract base class.

BasePage was merged into AbstractPage with the same side effect on inheriting
the object manager on Page, Link, etc.

BaseGallery was merged into AbstractGallery, no point in keeping it.

I didn't change imports or references in the admin modules, I followed
UserAdmin conventions here. You'll have to explicitly register, e.g.  PageAdmin
to your swapped Page model.

No docs update yet, let's first agree on code and naming conventions, what will
become the public API for users. This includes whether the docstring goes on
the abstract model or the vanilla model.

I also guess this deprecates the field injection feature, before removing it in
a future major version.

Besides that, it must be 100% compatible, no test changes apart from imports.
  • Loading branch information
Hervé Cauwelier committed Feb 12, 2017
1 parent e574490 commit 118ba9b
Show file tree
Hide file tree
Showing 42 changed files with 343 additions and 125 deletions.
13 changes: 2 additions & 11 deletions mezzanine/accounts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"""
from __future__ import unicode_literals

from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ImproperlyConfigured

from mezzanine.utils.importing import import_dotted_path
from mezzanine.utils.models import get_swappable_model


class ProfileNotConfigured(Exception):
Expand All @@ -25,19 +25,10 @@ def get_profile_model():
``settings.ACCOUNTS_PROFILE_MODEL``, or ``None`` if no profile
model is configured.
"""

if not getattr(settings, "ACCOUNTS_PROFILE_MODEL", None):
raise ProfileNotConfigured

try:
return apps.get_model(settings.ACCOUNTS_PROFILE_MODEL)
except ValueError:
raise ImproperlyConfigured("ACCOUNTS_PROFILE_MODEL must be of "
"the form 'app_label.model_name'")
except LookupError:
raise ImproperlyConfigured("ACCOUNTS_PROFILE_MODEL refers to "
"model '%s' that has not been installed"
% settings.ACCOUNTS_PROFILE_MODEL)
return get_swappable_model("ACCOUNTS_PROFILE_MODEL")


def get_profile_for_user(user):
Expand Down
9 changes: 9 additions & 0 deletions mezzanine/blog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@
from __future__ import unicode_literals

from mezzanine import __version__ # noqa
from mezzanine.utils.models import get_swappable_model


def get_post_model():
return get_swappable_model("BLOG_POST_MODEL")


def get_category_model():
return get_swappable_model("BLOG_CATEGORY_MODEL")
11 changes: 8 additions & 3 deletions mezzanine/blog/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
from django.utils.feedgenerator import Atom1Feed
from django.utils.html import strip_tags

from mezzanine.blog.models import BlogPost, BlogCategory
from mezzanine.blog import get_post_model, get_category_model
from mezzanine.conf import settings
from mezzanine.core.templatetags.mezzanine_tags import richtext_filters
from mezzanine.core.request import current_request
from mezzanine.generic.models import Keyword
from mezzanine.pages import get_page_model
from mezzanine.utils.html import absolute_urls
from mezzanine.utils.models import pages_installed
from mezzanine.utils.sites import current_site_id


Expand Down Expand Up @@ -43,8 +45,8 @@ def __init__(self, *args, **kwargs):
super(PostsRSS, self).__init__(*args, **kwargs)
self._public = True
page = None
if "mezzanine.pages" in settings.INSTALLED_APPS:
from mezzanine.pages.models import Page
if pages_installed():
Page = get_page_model()
try:
page = Page.objects.published().get(slug=settings.BLOG_SLUG)
except Page.DoesNotExist:
Expand Down Expand Up @@ -79,12 +81,14 @@ def link(self):
def items(self):
if not self._public:
return []
BlogPost = get_post_model()
blog_posts = BlogPost.objects.published().select_related("user"
).prefetch_related("categories")
if self.tag:
tag = get_object_or_404(Keyword, slug=self.tag)
blog_posts = blog_posts.filter(keywords__keyword=tag)
if self.category:
BlogCategory = get_category_model()
category = get_object_or_404(BlogCategory, slug=self.category)
blog_posts = blog_posts.filter(categories=category)
if self.username:
Expand All @@ -105,6 +109,7 @@ def item_description(self, item):
def categories(self):
if not self._public:
return []
BlogCategory = get_category_model()
return BlogCategory.objects.all()

def feed_url(self):
Expand Down
3 changes: 2 additions & 1 deletion mezzanine/blog/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from django import forms

from mezzanine.blog.models import BlogPost
from mezzanine.blog import get_post_model
from mezzanine.core.models import CONTENT_STATUS_DRAFT


BlogPost = get_post_model()
# These fields need to be in the form, hidden, with default values,
# since it posts to the blog post admin, which includes these fields
# and will use empty values instead of the model defaults, without
Expand Down
8 changes: 6 additions & 2 deletions mezzanine/blog/management/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@
from django.utils.encoding import force_text
from django.utils.html import strip_tags

from mezzanine.blog.models import BlogPost, BlogCategory
from mezzanine.blog import get_post_model, get_category_model
from mezzanine.conf import settings
from mezzanine.core.models import CONTENT_STATUS_DRAFT
from mezzanine.core.models import CONTENT_STATUS_PUBLISHED
from mezzanine.generic.models import Keyword, ThreadedComment
from mezzanine.pages.models import RichTextPage
from mezzanine.pages import get_rich_text_page_model
from mezzanine.utils.html import decode_entities


User = get_user_model()
BlogPost = get_post_model()
BlogCategory = get_category_model()
RichTextPage = get_rich_text_page_model()


class BaseImporterCommand(BaseCommand):
Expand Down
1 change: 1 addition & 0 deletions mezzanine/blog/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class Migration(migrations.Migration):
('user', models.ForeignKey(related_name='blogposts', verbose_name='Author', to=settings.AUTH_USER_MODEL)),
],
options={
'swappable': 'BLOG_POST_MODEL',
'ordering': ('-publish_date',),
'verbose_name': 'Blog post',
'verbose_name_plural': 'Blog posts',
Expand Down
20 changes: 17 additions & 3 deletions mezzanine/blog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
from mezzanine.utils.models import AdminThumbMixin, upload_to


class BlogPost(Displayable, Ownable, RichText, AdminThumbMixin):
class AbstractBlogPost(Displayable, Ownable, RichText, AdminThumbMixin):
"""
A blog post.
"""

categories = models.ManyToManyField("BlogCategory",
categories = models.ManyToManyField(settings.BLOG_CATEGORY_MODEL,
verbose_name=_("Categories"),
blank=True, related_name="blogposts")
allow_comments = models.BooleanField(verbose_name=_("Allow comments"),
Expand All @@ -33,6 +33,7 @@ class BlogPost(Displayable, Ownable, RichText, AdminThumbMixin):
admin_thumb_field = "featured_image"

class Meta:
abstract = True
verbose_name = _("Blog post")
verbose_name_plural = _("Blog posts")
ordering = ("-publish_date",)
Expand Down Expand Up @@ -64,16 +65,29 @@ def get_absolute_url(self):
return reverse(url_name, kwargs=kwargs)


class BlogCategory(Slugged):
class BlogPost(AbstractBlogPost):

class Meta(AbstractBlogPost.Meta):
swappable = 'BLOG_POST_MODEL'


class AbstractBlogCategory(Slugged):
"""
A category for grouping blog posts into a series.
"""

class Meta:
abstract = True
verbose_name = _("Blog Category")
verbose_name_plural = _("Blog Categories")
ordering = ("title",)

@models.permalink
def get_absolute_url(self):
return ("blog_post_list_category", (), {"category": self.slug})


class BlogCategory(AbstractBlogCategory):

class Meta(AbstractBlogCategory.Meta):
swappable = 'BLOG_CATEGORY_MODEL'
4 changes: 3 additions & 1 deletion mezzanine/blog/templatetags/blog_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
from django.contrib.auth import get_user_model
from django.db.models import Count, Q

from mezzanine.blog import get_post_model, get_category_model
from mezzanine.blog.forms import BlogPostForm
from mezzanine.blog.models import BlogPost, BlogCategory
from mezzanine.generic.models import Keyword
from mezzanine import template

User = get_user_model()
BlogPost = get_post_model()
BlogCategory = get_category_model()

register = template.Library()

Expand Down
12 changes: 9 additions & 3 deletions mezzanine/blog/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@

from django.core.urlresolvers import reverse

from mezzanine.blog.models import BlogPost
from mezzanine.blog import get_post_model
from mezzanine.conf import settings
from mezzanine.core.models import CONTENT_STATUS_PUBLISHED
from mezzanine.pages.models import Page, RichTextPage
from mezzanine.pages import get_page_model, get_rich_text_page_model
from mezzanine.utils.models import pages_installed
from mezzanine.utils.tests import TestCase


BlogPost = get_post_model()
Page = get_page_model()
RichTextPage = get_rich_text_page_model()


class BlogTests(TestCase):

def test_blog_views(self):
Expand All @@ -34,7 +40,7 @@ def test_blog_views(self):
self.assertEqual(response.status_code, 200)

@skipUnless("mezzanine.accounts" in settings.INSTALLED_APPS and
"mezzanine.pages" in settings.INSTALLED_APPS,
pages_installed(),
"accounts and pages apps required")
def test_login_protected_blog(self):
"""
Expand Down
2 changes: 2 additions & 0 deletions mezzanine/blog/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ class TranslatedBlogPost(TranslatedDisplayable, TranslatedRichText):
class TranslatedBlogCategory(TranslatedSlugged):
fields = ()


# XXX What about swapped models?
translator.register(BlogCategory, TranslatedBlogCategory)
translator.register(BlogPost, TranslatedBlogPost)
4 changes: 3 additions & 1 deletion mezzanine/blog/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _

from mezzanine.blog.models import BlogPost, BlogCategory
from mezzanine.blog import get_post_model, get_category_model
from mezzanine.blog.feeds import PostsRSS, PostsAtom
from mezzanine.conf import settings
from mezzanine.generic.models import Keyword
from mezzanine.utils.views import paginate

User = get_user_model()
BlogPost = get_post_model()
BlogCategory = get_category_model()


def blog_post_list(request, tag=None, year=None, month=None, username=None,
Expand Down
3 changes: 2 additions & 1 deletion mezzanine/boot/lazy_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.shortcuts import redirect

from mezzanine.utils.importing import import_dotted_path
from mezzanine.utils.models import pages_installed


class LazyAdminSite(AdminSite):
Expand Down Expand Up @@ -104,7 +105,7 @@ def urls(self):
url("^displayable_links.js$", displayable_links_js,
name="displayable_links_js"),
]
if "mezzanine.pages" in settings.INSTALLED_APPS:
if pages_installed():
from mezzanine.pages.views import admin_page_ordering
urls.append(url("^admin_page_ordering/$", admin_page_ordering,
name="admin_page_ordering"))
Expand Down
3 changes: 2 additions & 1 deletion mezzanine/core/management/commands/createdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.db import connection

from mezzanine.conf import settings
from mezzanine.galleries import get_gallery_model
from mezzanine.utils.tests import copy_test_to_media


Expand Down Expand Up @@ -115,7 +116,7 @@ def create_pages(self):
if self.verbosity >= 1:
print("\nCreating demo pages: About us, Contact form, "
"Gallery ...\n")
from mezzanine.galleries.models import Gallery
Gallery = get_gallery_model()
call_command("loaddata", "mezzanine_optional.json")
zip_name = "gallery.zip"
copy_test_to_media("mezzanine.core", zip_name)
Expand Down
8 changes: 4 additions & 4 deletions mezzanine/core/sitemaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from django.contrib.sitemaps import Sitemap
from django.contrib.sites.models import Site

from mezzanine.conf import settings
from mezzanine.core.models import Displayable
from mezzanine.utils.models import blog_installed
from mezzanine.utils.sites import current_site_id


blog_installed = "mezzanine.blog" in settings.INSTALLED_APPS
if blog_installed:
from mezzanine.blog.models import BlogPost
if blog_installed():
from mezzanine.blog import get_post_model
BlogPost = get_post_model()


class DisplayableSitemap(Sitemap):
Expand Down
Loading

0 comments on commit 118ba9b

Please sign in to comment.