Building a full ecommerce site part 1: integrating Django-Oscar into Mezzanine

Why this article

Because Stack Overflow is feckin' unfriendly to noobs and I need somewhere to post my code so I can show it to more experienced devs and ask if I'm doing it right or is some other method better practice.

I spend like two hours meticulously crafting questions and making sure they're concise and have the right details and everything and some eejit comes along and downvotes them without so much as a squeak of explanation. Do you know if you get downvoted enough you get banned from asking questions for SIX MONTHS?

If you're unable to improve your existing questions, you'll get the chance to ask a new one 6 months after your last question. If that question is positively received, you may be able to continue asking questions; if not, then the ban will be reinstated.

- Stack Overflow question ban

Makes me so angry.

And, I couldn't find a tutorial on how to integrate Oscar and Mezzanine.

And, and, if this helps other noobs it's nice too.

Why Django-Oscar + Mezzanine

Important: I used Oscar 1.5.2 and Mezzanine 4.2.3. I think one of them installed Django 1.10.8 for common use.

I have to make a full ecommerce. I settled on Django-Oscar because it seems to be the best maintained with the most contributers. The documentation is not great, perhaps especially for beginners, but it's still better than that of all the other Django-based options.

The site needs a blog. I picked Mezzanine because I'm using it for other sites right now and I find it a full and wonderfully built CMS.

Objective

I messed around with Django-Oscar for a couple of days trying to get a little bit familiar with it. Then, I started my first attempt to combine it with Mezzanine yesterday.

My first objective: make a link to the Mezzanine blog on the Oscar front end navbar, and to have the blog load properly.

I thought it'd take a max of two hours. Took me two days. Not bad, really. At least for now.

Step 1: standard stuff

Create a virtual environment and pip install Django-Oscar and Mezzanine.

Step 2: start a Mezzanine project

mezzanine-project mezzanine_plus_oscar. I didn't plan to be creative with names for this prototype.

Step 3: start integrating Oscar into Mezzanine

Follow the Django-Oscar documentation on getting started to integrate it into Mezzanine. Remember to add either localhost or 127.0.0.1 to ALLOWED_HOSTS in local_settings.py, and to createsuperuser.

Django complained that I can't concatenate Oscar's get_core_apps() to INSTALLED_APPS because INSTALLED_APPS is a tuple. I simply replaced its parentheses with square brackets and turned it into a list.

I was lazy and didn't create an Oscar user. I logged into Mezzanine at /admin, and then simply went to Oscar at /dashboard whenever I wanted to do something in the shop.

Then go to step 4.

Step 4: collect the Mezzanine templates

I learnt this from a Josh Cartmell articlepython manage.py collecttemplates. They are collected into a directory called templates in our project directory.

Step 5: collect the Oscar templates

I decided to go with Method 2 in the Oscar doc on How to customise templates. After following the instructions on the page to add OSCAR_MAIN_TEMPLATE_DIR to settings.py, I copied Oscar's templates at Python3/Lib/site-packages/oscar/templates/oscar to my project's template directory at mezzanine_plus_oscar/templates/oscar. I intended to edit these copied Oscar files so that the changes show on the site in accordance with Django's template extension rules.

However, this location is temporary. Since my settings.py file locates the templates directory in the main project directory, I must add the edited template files one level down for the changes to take effect. That is, I must add them to mezzanine_plus_oscar/templates instead of mezzanine_plus_oscar/templates/oscar. I'm being conservative right now because I suspect Mezzanine and Oscar share the same names for some templates.

Step 6: add Oscar's pages to the front end navbar

We create pages in Oscar by going to its Dashboard > Content > Pages. However, unlike Mezzanine, Oscar doesn't automagically add the pages we create to the front end navbar. I did a bit of googly and found this thread on the Django-Oscar Google group, and this solution on Stack Overflow. Now is the time to copy the required template over from  mezzanine_plus_oscar/templates/oscar tmezzanine_plus_oscar/templates.

The template in question is mezzanine_plus_oscar/templates/oscar/partials/nav_primary.html. I copied the entire partials directory and put them in mezzanine_plus_oscar/templates.

Then, I opened nav_primary.html and did the following:

<!-- Look for this block near the bottom -->
{% block nav_extra %}

<!-- Add this for loop -->
{% for page in flatpages %}
<li><a href="{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}

{% endblock %}

This adds the pages we create to the front end navbar.

Step 7: make Mezzanine blog templates inherit from Oscar's templates

Mezzanine has blog templates in mezzanine_plus_oscar/templates/blog: blog_post_detail.html and blog_post_list.html.

I'm not sure how exactly I edited the starting part of blog_post_list.html, but the important thing is that I did this:

<!-- Replace base.html with layout.html for extension -->
{% extends "layout.html" %}

...

<!-- Add block content tag above breadcrumb tag -->

{% block content %}
{% block breadcrumb_menu %}

...

<!-- Add endblock tag for block content at the very end-->
{% endblock %}

Yes, copy layout.html from mezzanine_plus_oscar/templates/oscar over to mezzanine_plus_oscar/templates.

I think I also edited the {% block title %} lines because it was showing something funky but I forget. The breadcrumb lines may also have been copied over from Oscar. I'm not sure. I'm sorry! It was stressful, ok! Anyway, it's pretty easy to figure out. I hope.

Next, something similar for blog_post_detail.html. The only big difference is that this time, I remember what I did to {% block title %}:

<!-- Replace base.html with layout.html for extension -->
{% extends "layout.html" %}

{% block title %}
{% editable blog_post.title %} <!-- DELETE THIS LINE -->
{{ blog_post.title }}
{% endeditable %} <!-- DELETE THIS LINE -->
{% endblock %}

...

<!-- Add block content tag above breadcrumb tag -->
{% block content %}
{% block breadcrumb_menu %}

...

<!-- Add endblock tag for block content at the very end-->
{% endblock %}

Step 8: Redirect Oscar's blog page to Mezzanine's blog

Create the blog page at Dashboard > Content > Pages. I made the url /blog. Do NOT create a blog page in Mezzanine, otherwise, there'll be some sort of conflict. I'm not sure what its exact nature is but I think Mezzanine's urls will conflict with Oscar's urls. I dug into the source code of both packages to take a look but I guess I'm just not good enough to find the exact lines of conflicting code yet.

Step 9: redirect Oscar's /blog page to Mezzanine's blog. This was really tricky for me because I'm a feckin' noob. So many goddamn mistakes. And, I'm not sure I have my final satisfactory solution yet. Anyway, in the projects urls.py:

# Changed this to "index" because I wanted the shop to occupy the main domain.
url("index", direct_to_template, {"template": "index.html"}, name="home"),

# Oscar's urls, left as-is.
url("", include(application.urls)),

# Redirect from /blog to /mez/blog, which is the Mezzanine blog.
url(r"^blog/$", RedirectView.as_view(url="/mez/blog/", permanent=False), name='go-to-mezblog'),

...

# Change this to "mez/" so the Mezzanine blog url becomes "/mez/blog".
url("^mez/", include("mezzanine.urls")),

I found the redirecting solution on Stack Overflow. The original Django documentation on RedirectView is concise and worth reading as well because it contains some attributes we might find useful in future.

Instead of changing the url for mezzanine.urls to ^mez/, another method is to mount Mezzanine under a prefix, which is explained in the comments in urls.py right under url("^", include("mezzanine.urls")).

Alternative step 8: Redirect from /mez/blog to /blog

Notice that I've changed the url for mezzanine.urls to ^mez/ in the last line above. This makes Django prefix all Mezzanine's urls with mez/. For instance, the Mezzanine blog is now located at localhost:8000/mez/blog. And, we're redirected to this url whenever we click on localhost:8000/blog link on the Oscar front end navbar.

Another way of doing it is to leave Mezzanine's urls unchanged as url("^", include("mezzanine.urls")) , make a page in Oscar with the url /mez/blog, or whatever you want, and then redirect from /mez/blog to /blog like this: url(r"^mez/blog/$", RedirectView.as_view(url="/blog/", permanent=False), name='go-to-mezblog').

The trouble with this, though, is that both Oscar and Mezzanine's urls would be located at the root directory because:

url("", include(application.urls)),
...
url("^", include("mezzanine.urls")),

I think this will make deconflicting urls a pain later on. So, I guess, short of digging into the source views and rewriting them, we should make either Mezzanine or Oscar's urls go to a specific prefix.

That's it! Phew!

Now, we have a Mezzanine blog attached to our Oscar shop. Add blog entries at /admin as is usual with Mezzanine. Access your shop at /dashboard as is usual with Oscar.

I'm not very happy with the fact that /blog is redirected to /mez/blog. But I couldn't find documentation on how to have the Mezzanine blog load on Oscar when people visit /blog, and without the two packages' urls conflicting. Probably have to dig deep into the views of both packages. Maybe somebody can show me. Doing things alone can be exhausting and I'm stopping here for the day.

Next up, integrating Paypal and Stripe, for which I've had no end of problems. But, we forge ahead, huh?

Comments

Comment awaiting approval 2 weeks, 3 days ago

New Comment

required

required (not published)

optional

required