Blog | Phodal - A Growth Engineerhttp://www.phodal.com/blog/2018-01-30T14:10:26.692682+00:00Blog从遗留技术栈升级里,我学到的八件事2018-01-30T14:09:42+00:002018-01-30T14:10:26.692682+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/learning-from-blogpost-tech-stack-migrate/几周前,当我使用 Mifa 主题刷新我的博客时,我发现了一件不得了的事情:我的博客使用的 Python 版本是 2.7,而不是我预期的 3.5。并且我用的 Django 版本是 1.9,它是 2015 年的版本。这些让我意识到,如果我再不做点什么,我的博客可能就维护不了了。
毕竟 Django 已经 2.0 了,而 Python 2.7 即将(好多年了)成为过去式了。
尽管我在之前的文章,讲述了一系列的遗留系统的问题,其中之一就是遗留技术栈。因为技术栈老旧而导致系统难以维护,并不是我们想要的结果,只是当它并不是我们的第一优先级事务时,短期的技术栈老旧也是可以接受的。
于是,我决定要去升级我的博客的技术栈了,理想中的步骤很简单:
1. 升级核心框架
2. 迁移应用代码
3. 迁移数据库
只是在这个过程中,遇到一系列的坑。真实的步骤如下所示:
1. 创建全新的环境
2. 使用线上的数据进行测试及数据库迁移
3. 更细力度的版本管理控制,方便回滚
4. 优先升级次要组件的版本,以方便向上兼容性
5. 一步步升级核心框架
6. 必要时,自己编写组件代码
7. 清理掉不需要的代码、文件
8. 上线前使用线上的环境进行预部署
接着,让我来一步步复述这个过程。
1. 创建全新的环境
---
如上一篇文章《[荐书《遗留系统:重建实战》:当你面对一坨代码时,你应该这么做](https://www.phodal.com/blog/recommend-books-legacy-system-re-enginning/) 所说,我们应该对环境搭建这个任务进行计时,以便于找出系统的瓶颈。
首先,我遇到的第一个问题是:MBP 上没有环境。因为之前笔记本的硬盘坏了,上面的大部分资料都丢失了,因此我需要从头搭建一个环境。
对于我而言,这是一次相当不错的机会,它可以验证系统的本地配置是不是正确的。然后,我发现新的系统上也没有安装 virtualenv,于是不得不重从搭建一个环境。
然后,我按照我在博客源码上的 README,发现并没有搭建成功。这种感觉就像你拿着一本产品说明书,发现上面说的都是错的一样。
简单的总结一下这个阶段的几个原因:
- 本地配置文件 ``local_settings.py`` 文件不存在
- 没有对数据为空的情形进行判断
当然还有其它一些问题,让我再细细说来。
2. 使用线上的数据进行测试及数据库迁移
---
在我早期的项目中,我们一般会拥有一份半年前的线上环境,用于在 staging(模拟环境)环境上进行测试。它可以用数据证明,这些功能本身是相当可靠的。
搭建环境的过程中,我尝试创建一个全新的测试数据库。但是,想一想发现这样做容易出现问题,于是便想找份线上的环境的数据库进行测试。虽然我并没有将我的各种环境充分的自动化,但是我会定期手动将数据库(SQLite3)备份到 AWS S3 上——我最初选择使用 SQLite3 的主要原因是,备份方便,不需要占用额外的服务器资源。**对于一般的应用来说,使用 MySQL/MariaDB 才是正确的选择**。SQLite 3 是我在博客设计初期做的一个错误的决策,当时 Too Young。未来,可能会将数据库切换到 DynamoDB 上。
再回到线上数据的这一问题上,除了应对数据本身的变化之外。还有一点是,Django 或者 Mezzanine 在升级的时候,都需要进行**数据库迁移**。
我之前将 Mezzanine 1.3 升级到 Mezzanine 3.0 的过程中,遇到一系列的数据库问题,最后不得不重建数据库。这个惨痛的经历告诉了我,数据库迁移是一个大坑。
3. 更细力度的版本管理控制,方便回滚
---
是的,即使你更新了个小依赖,也要确保使用了版本管理。它可以让我们随时能回滚到上一次个性,以确保迁移的正常。
如果你在某一时刻,你同时更新了 A、B、C 依赖,那么可能因此修改大量的代码。而在修改代码的过程中,一旦出错的话,那么回滚的难度就变得相当的大。
于是我提交的步伐,比以往的正常时候都更小了。小到只更新一行配置,我也会做一个提交。
反正最后是迁移成功了,不能证明这个策略是不是对的。但是,这样做是对的。
4. 优先升级次要组件的版本,以方便向上兼容性
---
对于项目中用到的一些辅助软件,如使用 ``djangorestframework`` 作为 RESTful API 框架,我优先升级了它们。对于这些框架而言,他们在兼容低版本的同时,也会兼容更版本的软件。但是高版本的 Django,有可能并不会兼容低版本的其它软件。因此,优先升级这些组件,可以保证核心组件可以更容易迁移。而不是在升级核心框架的同时,查看是否有这些次要组件带来的问题。
过去我一般不会采用固定依赖版本的方式来运行部署,如 Ruby 中的 Gemfile,Node.js 中的 Yarn.lock,Python 中采用的方式是:
```
django==1.10.7
Mezzanine==4.2.3
bleach==1.5
djangorestframework==3.7.7
django-uuslug
django-cors-headers
djangorestframework-jwt
django-widget-tweaks==1.4.1
markdown==2.6.11
```
如上所见,我只会在重要的组件中,采用 fix 的版本号,主要是为了方便升级。对于次要组件来主,这种策略相当的成功。
5. 一步步升级核心框架
---
好了,现在到了最大的坑里,升级 Django 版本。
事实证明,直接对着 CHANGELOG 来修改代码,是最简单的一种升级方式。
起先我直接改 Django 改成了 2.0.1 版本,发现一系列的不兼容——主要是 Mezzanine CMS 引起的问题,于是只能转到 1.10.7。
然而,从 1.9.6 到 1.10.7 算是一个大的升级,为 2.0 移除了一系列不兼容的 API,如:
- 新的 TEMPLATES 配置,旧的版本中使用多个模板配置,而新的版本中只需要一个配置即可。
- 旧的 ``urls.py`` 全部升级,在新的 Django 中统一了路由的写法。
- 不再需要的 ``future`` 标签,future 可以在低版本的 Python 上运行一些新的语言特性。
在迁移的过程中,还发现了一个第三方组件不支持新版本的 Django。
6. 必要时,自己编写组件代码
---
使用开源软件,便意味着:在你使用的过程中,作者有可能随时会弃坑;因此,随时要做好填坑的准备。在我迁移的过程中,也遇到了这样的问题。
默认使用的 markdown 编辑器,``mezzanine_pagedown`` 中的 ``urls.py`` 使用的 pattern 已经被抛弃了。而这个 repo 几乎已经陷入了不维护的状态,于是我只得引入这个库到我的项目中,然后自己去修复这些问题。
好在升级起来还是蛮很容易的,后来仔细一读代码发现,这个库只是对 markdown 库进行了一些封装。因此,自己动手写了一个 markdown 的封装。
7. 清理掉不需要的代码、文件
---
早期使用 ``python manage.py collectstatic`` 的时候,留下了不同版本的静态文件,如早期的 jQuery 1.4.2、jquery-1.7.1.min.js、jquery-1.7.2.min.js 等等。
因此,便直接删除了旧的静态文件,这些文件有:
- 静态文件库
- 不使用的代码
就现在而言,清理这些旧文件,并不会带来额外的收益。但是,未来就不好说了。
8. 上线前使用线上的环境进行预部署
---
我在我的服务器上获取最新的版本,创建了新的虚拟环境,然后测试:
``python manage.py runserver 8888``
代码看来似乎是好的,于是我更新了启动脚本里的 ``虚拟环境`` 的路径,并使用启动脚本来运行服务。结果,系统并没有启动起来——原因是少了 gunicorn。
我忘了在 ``requirements.txt`` 中加油入 ``gunicorn`` 的依赖,
于是,我重新启动了服务,打开了 phodal.com,发现 500 了又。mdzz,
去看了看日志,发现线上使用了 memcached 作为数据缓存,这个配置写在 ``local_settings.py`` 文件中,但是没有添加在依赖中。
怪我咯。博客反爬虫 策略二——Django RSS添加原文链接2015-04-11T13:40:46+00:002015-04-29T11:28:16.505721+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/anti-botos-by-add-origin-link-and-limit-charater/看了看第一篇,想了想过去写爬虫的策略是一个个元素的找,再看看[《策略一——根据User Agent》](http://www.phodal.com/blog/nginx-disable-crawlers-by-user-agent/)。看来是不能实现的,想了想,最后只想到了RSS订阅这事。
最后效果图:
![anti crawler from rss][1]
[1]: /static/media/uploads/anti_crawler.jpg
##策略考虑
看了看所谓的垃圾站的一些文章,发现都是原文的,也没有一些特有的元素。有种用RSS订阅过来的感觉,那么显然在我们的文章加一个原文链接的作用可能不是很大。
- 如果对方的rank比你高,你的文章就很容易被Google定为抄袭
- 对方可以在link上加nofollow,Google从理论上是支持这个的,实现有可能会继续往下走。
于是策略上就有两部分:
- 减少原文字数
- 加原文链接
不良影响:
- 影响RSS阅读用户。
但是从理论上来说,如果用户有兴趣的话,读原文可能是不错的。只能后面再看看,是否真的有影响了。
##Mezzanine Django RSS 扩展
看了看Mezzanine的feed代码,发现改起来也不难。
###添加url route
在根目录的``urls.py``中添加
urlpatterns += patterns("feed.view",
url("^blog/feeds/(?P<format>.*)%s$" % "/",
"blog_post_feed", name="blog_post_feed")
)
###添加package
新建一个python package,结构如下:
|______init__.py
|____feeds.py
|____view.py
我们的``view.py``内容是:
from __future__ import unicode_literals
from django.http import Http404
import feeds
def blog_post_feed(request, format, **kwargs):
try:
return {"rss": feeds.PostsRSS, "atom": feeds.PostsAtom}[format](**kwargs)(request)
except KeyError:
raise Http404()
与之对应的feeds.py则大部分源自``mezzanine.blog.feeds``,除了修改了``item_description``方法,原来的方法是
def item_description(self, item):
return richtext_filters(item.content)
变成了
def item_description(self, item):
copy_info = "\n原文:[" + \
super(PostsRSS, self).item_title(item) + "](" + \
super(PostsRSS, self).item_link(item) + ")"
return richtext_filters(item.content[0:300]) + richtext_filters(copy_info)
###变化
- 添加了原文链接
- 限定了字符数300
</format>Django & Mezzanine Sitemap 扩展2014-12-28T13:12:32+00:002014-12-28T13:33:12.416224+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/mezzanine-sitemap-add-changefreq-and-priority/在查看[寻ta驿站](http://www.xuntayizhan.com/)在谷歌的结果时,发现其有着良好的目录结构,再看看现在的博客,看上去有点乱。或者说现在的sitemap做得很烂:
- 没有优先极
- 没有更新期限
打开Mezzanine的github,里面有关于这的issues,结果上面写着
> While these would be useful, I actually think most people aren't concerned with them and they'd contribute to cluttering the admin, even only slightly.
作者认为这东西不是很重要,或者说优先级不高。。
##Django Sitemap
在Django中,我们添加一个新的Sitemap只需要这样子。
```python
from django.contrib.sitemaps import Sitemap
from blog.models import Entry
class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return Entry.objects.filter(is_draft=False)
def lastmod(self, obj):
return obj.pub_date
```
看上去似乎很简单,然而我们需要在Mezzanine中重新修改他们。。
#Mezzanine Sitemap
##原始Mezzanine Sitemap
在查看代码的时候发现``sitemap.xml``是在``urls.py``在是这样配置的
sitemaps = {"sitemaps": {"all": DisplayableSitemap}}
urlpatterns += patterns("django.contrib.sitemaps.views",
("^sitemap\.xml$", "sitemap", sitemaps)
)
最后调用了``DisplayableSitemap``,而``DisplayableSitemap``函数的代码如下所示
```python
class DisplayableSitemap(Sitemap):
def items(self):
return list(Displayable.objects.url_map(in_sitemap=True).values())
def lastmod(self, obj):
if blog_installed and isinstance(obj, BlogPost):
return obj.updated or obj.publish_date
def get_urls(self, **kwargs):
kwargs["site"] = Site.objects.get(id=current_site_id())
return super(DisplayableSitemap, self).get_urls(**kwargs)
```
和Django的Sitemap示例一比,果然``没有优先级,没有更新频率``
##Mezzanine Sitemap Extend
新建了一个Django App
```
.
|______init__.py
|____sitemaps.py
```
和大多数app一样``__init__.py``是空的,而sitemaps.py是从``DisplayableSitemap``复制过来的:
添加了一个``changefreq``,当对象是不同类型的时候返回不同的值:
def changefreq(obj):
if isinstance(obj, BlogPost):
return "Monthly"
if isinstance(obj, BlogCategory):
return "Weekly"
if isinstance(obj, Page):
return "Weekly"
return "Daily"
以及一个``priority``,最高自然是``homepage``:
def priority(obj):
if isinstance(obj, BlogPost):
return "0.2"
if isinstance(obj, BlogCategory):
return "0.3"
if isinstance(obj, Page):
return "0.6"
return "1.0"
修改了items,添加了目录
def items(self):
blogpost_with_page = list(Displayable.objects.url_map(in_sitemap=True).values())
category = list(BlogCategory.objects.all())
return blogpost_with_page + category
这样就大功告成了。
##其他
最后效果可见[http://www.phodal.com/sitemap.xml](http://www.phodal.com/sitemap.xml)
代码见:[https://github.com/phodal/phodaldev/blob/master/sitemaps/sitemaps.py](https://github.com/phodal/phodaldev/blob/master/sitemaps/sitemaps.py)谷歌 Microdata之站内搜索2014-12-21T09:25:29+00:002014-12-21T09:49:23.598255+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/mezzanine-add-google-sitelinks-search-box/自从上次谷歌把头像从搜索结果中移除后,有些时候没有关注Google MicroData了。现在,发现谷歌可以支持站内搜索,如下图所示。
在搜索之前Review的《Learning Internet of Things》一书的时候发现Packet出版社也多出了搜索框。
![Packt][1]
于是,觉得些事必有蹊跷。
#谷歌 Sitelinks Search Box#
##Sitelinks搜索框简介##
官方有这样的简介:
**With Google sitelinks search box, people can reach your content more quickly from search results. Search users sometimes use navigational queries—typing in the brand name or URL of a known site—only to do a more detailed query once on that site. For example, suppose someone wants to find that video about the guilty dog on YouTube. They type YouTube, or you-tube, or youtube.com into Google Search, follow the link to YouTube, and then actually search for the dog video.**
> The sitelinks search box removes that extra step: a query for youtube displays a site-specific search box in the sitelinks section, so the user can immediately search for that guilty dog video without having to click through to the site.
与谷歌网站的搜索框,可以达到你的内容更快速的搜索结果。搜索用户有时使用导航查询在一个已知的网站只做一个更详细的查询一次,网站的品牌名称或URL打字。例如,假设有人想找到关于YouTube上的视频有罪的狗。他们类型的YouTube,或你管,或youtube.com在谷歌搜索,链接到YouTube,然后其实搜狗视频。
简单地来说,就是我们不再需要跳到某个网站,然后点搜索。
而这东西叫做: Sitelinks Search Box
##Google Sitelinks实战
过程一共是三步,实际上可以说只有一步,就是添加``Microdata``:
1.安装搜索引擎
(ps:对于像我这样已经有搜索功能的就不需要这么麻烦)
2.用Microdata或JSON-LD标记元素
**JSON-LD的示例**
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebSite",
"url": "http://www.phodal.com/",
"potentialAction": {
"@type": "SearchAction",
"target": "http://www.phodal.com/search/?q={search_term_string}",
"query-input": "required name=search_term_string"
}
}
</script>
**MicroData示例**
```html
<div itemscope="" itemtype="http://schema.org/WebSite">
<meta content="https://www.phodal.com/" itemprop="url"/>
<form itemprop="potentialAction" itemscope="" itemtype="http://schema.org/SearchAction">
<meta content="https://www.phodal.com/search/?q={search_term_string}" itemprop="target"/>
<input itemprop="query-input" name="search_term_string" required="" type="text"/>
<input type="submit"/>
</form>
</div>
```
3.耐心地等待谷歌把结果放进去。
##Mezzanine Google Sitelinks
在我的博客上,最后结果大概是这样的。``serach_form.html``
```html
{% load mezzanine_tags i18n future %}
<div itemscope="" itemtype="http://schema.org/WebSite">
<meta content="http://www.phodal.com/" itemprop="url"/>
<form %}"="" action="{% url " class="navbar-form navbar-right" itemprop="potentialAction" itemscope="" itemtype="http://schema.org/SearchAction" role="search" search"="">
<meta content="http://www.phodal.com/search/?q={search_term_string}" itemprop="target"/>
<div class="form-group">
<input %}"="" class="form-control" itemprop="query-input" name="search_term_string" placeholder="{% trans " required="" search"="" type="text" value="{{ request.REQUEST.q }}"/>
</div>
{% if search_model_choices %}
{% if search_model_choices|length == 1 %}
<input name="type" type="hidden" value="{{ search_model_choices.0.1 }}"/>
{% else %}
<div class="form-group">
<select class="form-control" name="type">
<option value="">{% trans "Everything" %}</option>
{% for verbose_name, model in search_model_choices %}
<option %}="" %}selected{%="" endif="" if="" model="request.REQUEST.type" value="{{ model }}" {%="">
{{ verbose_name }}
</option>
{% endfor %}
</select>
</div>
{% endif %}
{% endif %}
<input %}"="" class="btn btn-default" go"="" type="submit" value="{% trans "/>
</form>
</div>
```
##其他
这似乎是一个不错的功能,+ 1。只是对于像百度之类的似乎直接就可以了,不过对于广大的技术博客主业说可以说是一种福利。
[1]: /static/media/uploads/packt.jpgMezzanine 升级与Markdown库Pagedown 使用笔记2014-12-04T13:18:22+00:002014-12-04T13:36:07.103631+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/mezzanine-upgrade-and-use-mezzanine-pagedown/简单地对博客更新了一下,也发现了一些有意思的东西。
##Mezzanine RSS
发现的第一个有意思的是Mezzanine里的RSS是没有默认的禁用选项,有一个``BLOG_RSS_LIMIT``参数,但是设置为None的时候是返回所有的结果,于是将他改为1。
BLOG_RSS_LIMIT = 1
不过最开始的想法是,注释掉urls.py,但是总感觉改Repo,以后升级不爽、
#url("^%sfeeds/(?P<format>.*)%s$" % _slashes,
# "blog_post_feed", name="blog_post_feed"),
##Mezzanine Pagedown 使用
安装了新版本的Mezzanine,再直接安装Pagedown后,没有了将Markdown转换为HTML的功能。找到官网看了看配置,似乎是对的
RICHTEXT_WIDGET_CLASS = 'mezzanine_pagedown.widgets.PageDownWidget'
RICHTEXT_FILTER = 'mezzanine_pagedown.filters.custom'
PAGEDOWN_MARKDOWN_EXTENSIONS = ('extra','codehilite','toc')
RICHTEXT_FILTER_LEVEL = 3
PAGEDOWN_SERVER_SIDE_PREVIEW = True
发现还是不工作,只好一点点地删除代码,最后才发现在新版的Mezzanine中
register_setting(
name="RICHTEXT_FILTERS",
description=_("List of dotted paths to functions, called in order, on a "
"``RichTextField`` value before it is rendered to the template."),
editable=False,
default=("mezzanine.utils.html.thumbnails",),
)
给``RICHTEXT_FILTERS``设置了一个默认值``mezzanine.utils.html.thumbnails``,当我把默认值删了的时候就能正常工作了。于是试着重新配置了``Pagedown``:
RICHTEXT_WIDGET_CLASS = 'mezzanine_pagedown.widgets.PageDownWidget'
# RICHTEXT_FILTER = 'mezzanine_pagedown.filters.custom'
PAGEDOWN_MARKDOWN_EXTENSIONS = ('extra','toc')
RICHTEXT_FILTER_LEVEL = 3
PAGEDOWN_SERVER_SIDE_PREVIEW = True
RICHTEXT_FILTERS = (
"mezzanine.utils.html.thumbnails",
"mezzanine_pagedown.filters.extra",
"mezzanine_pagedown.filters.plain")
在``RICHTEXT_FILTERS``里把默认值加了进去。
> It Works
##其他
Mezzanine的当前版本是: ``3.1.10``
Django只支持到: ``1.6.8``,暂时不支持: ``1.7.1``
</format>Mint安装小记2014-06-05T13:33:12+00:002014-06-05T13:53:11.451287+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/mint-install-notes/安装Mint的过程没有什么好说的,装完机之后才是重点,这时继续默默地给自己的装机脚本写内容。
##Basic
配置只是一种习惯的迁移,大概就是这个意思,而在这里的脚本只是简单地做了几件事。或者说给几个软件进行配置:
- bash_it
- vim
- emacs
似乎没有太大的意义,只是bash似乎是平时用得较多的一个工具,和vim一样,至于Emacs今天也没有一个很好的位置和定位。比较复杂的东西算是bash_it了,当配置完bash_it的时候,我想基础环境我也相当于配置好了。。。里面有
- nodejs
- python
- chruby
所以算是包含了主流的一些平台和框架?当然还要有composer, 这个php的包管理工具。。于是最后会有
- npm
- pip
- rvm
- composer
真正的full stack需要的环境么?
##Software
接着算是一些常用软件了。
- Fcitx 输入法
- Chromium-browser 谷歌浏览器
- Arduino Arduino开发环境
- Retext Markdown的编辑器
- Skype 聊天。。
- Wesnoth 策略游戏
- Texlive-Full texlive环境
献上skype的截图
![Skyp Screenshot][1]
##Configure
最后脚本的内容大致如下所示
case $1 in
basic)
echo -e "\033[Install bash-it\033[0m"
git clone https://github.com/revans/bash-it .bash_it
sh ~/.bash_it/install.sh
echo -e "\033[Install janus\033[0m"
curl -Lo- https://bit.ly/janus-bootstrap | bash
;;
more) echo -e "\033[Install purcell emacs configure\033[0m"
git clone https://github.com/purcell/emacs.d .emacs.d
emacs
echo -e "\033[Install zsh configure\033[0m"
git clone https://github.com/robbyrussell/oh-my-zsh.git .oh-my-zsh
;;
chruby)
echo -e "\033[Install RVM\033[0m"
\curl -sSL https://get.rvm.io | bash -s stable
sudo gem install rake
wget -O chruby-0.3.8.tar.gz https://github.com/postmodern/chruby/archive/v0.3.8.tar.gz
tar -xzvf chruby-0.3.8.tar.gz
cd chruby-0.3.8/
sudo make install
rm -rf chruby-0.3.8
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
;;
drush)
curl -sS https://getcomposer.org/installer | php
sed -i '1i export PATH="$HOME/.composer/vendor/bin:$PATH"' $HOME/.bashrc
source $HOME/.bashrc
composer global require drush/drush:dev-master
;;
py)
sudo pip install argcomplete
sudo pip install mezzanine
sudo pip install pyserial
sudo pip install virtualenvwrapper
;;
ubuntu)
sudo apt-get install curl git python-setuptools emacs vim ruby
;;
*)
echo -e "\034[System Init Configure\033[0m"
echo -e "\034[Usage:{basic|more}\033[0m"
;;
esac
最后献上github地址
[https://github.com/gmszone/configure](https://github.com/gmszone/configure)
[1]: /static/media/uploads/2014-06-04_21:09:38%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE.pngDjango Sqlite3 MySQL迁移问题一览2014-05-21T12:22:29+00:002014-12-23T02:33:11.631311+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/question-django-mezzanine-sqlite3-migrate-mysql/往MySQL迁移的过程中遇到越来越多的问题。。
##MySQL Django 数据库引擎
> Django “Cannot add or update a child row: a foreign key constraint fails”
mysql> SHOW CREATE TABLE blog_blogcategory;
+-------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| blog_blogcategory | CREATE TABLE `blog_blogcategory` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`site_id` int(11) NOT NULL,
`title` varchar(500) NOT NULL,
`slug` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `blog_blogcategory_99732b5c` (`site_id`),
CONSTRAINT `site_id_refs_id_93afc60f` FOREIGN KEY (`site_id`) REFERENCES `django_site` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
也就是这个问题[a child row a foreign key constraint fails](http://stackoverflow.com/questions/6178816/django-cannot-add-or-update-a-child-row-a-foreign-key-constraint-fails)。
上面用的索引是InnoDB而有些是MyISAM
方法是在``settings.py``里添加
'OPTIONS': {
"init_command": "SET storage_engine=MyISAM",
},
于是``slave``就变成这样子了
"slave": {
# Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
"ENGINE": "django.db.backends.mysql",
# DB name or path to database file if using sqlite3.
"NAME": "phodal",
# Not used with sqlite3.
"USER": "root",
# Not used with sqlite3.
"PASSWORD": "",
# Set to empty string for localhost. Not used with sqlite3.
"HOST": "",
# Set to empty string for default. Not used with sqlite3.
"PORT": "",
'OPTIONS': {
"init_command": "SET storage_engine=MyISAM",
},
},
##MySQL Django 编码
Warning: Incorrect string value: '\xF0\x9F\x92\xB0' ...' for column 'content' at row 1
###MySQL utf8mb4
utf8mb4兼容utf8,且比utf8能表示更多的字符。
于是只能重来了。。。。。
CREATE DATABASE phodal CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
到此总算解决了大部分的文章问题了,然而还没有完。。。
##编码问题
UnicodeEncodeError: 'ascii' codec can't encode characters in position 7-10: ordinal not in range(128)
最后找到原因是...
全部文章(测试)
##不存在
> DoesNotExist: ContentType matching query does not exist.
Django Sqlite3 MySQL迁移2014-05-20T11:58:03+00:002014-05-20T12:22:20.324173+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/django-mezzanine-sqlite3-migrate-mysql/当我还在继续寻觅从SQLite3往MySQL迁移的时候,发现了这样一个文章:[Move django from SQLite to MySQL](http://macrotoma.blogspot.com/2012/10/solved-move-django-from-sqlite-to-mysql.html),于是试着开始做了。。
##最简单的SQLite MySQL迁移
这也是在绝大多数的搜索结果里面出现的,相仿或者是相似的方法。先dump再load
python ./manage.py dumpdata > data.json
接着再load数据
python ./manage.py loaddata data.json
然而正如作者所说的,不幸的是,这不会工作的。
##SQLite3 MySQL Migrate
1.编辑``settings.py``,添加``slave``数据库,
DATABASES = {'default': {...}, 'slave': {...}}
修改后大致如下所示
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "dev.db",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
},
"slave": {
"ENGINE": "django.db.backends.mysql",
"NAME": "phodal",
"USER": "root",
"PASSWORD": "",
"HOST": "",
"PORT": "",
},
}
思想上就是主从同步。
**如果使用south就先禁用**
2.创建数据库
CREATE DATABASE phodal CHARACTER SET utf8 COLLATE utf8_general_ci;
3.在新的数据库中创建表
python ./manage.py syncdb --database slave
**允许south``,再重复一次
python ./manage.py syncdb --database slave
4.创建``to_slave.py``在项目目录
from django.contrib.contenttypes.models import ContentType
def run():
def do(Table):
if Table is not None:
table_objects = Table.objects.all()
for i in table_objects:
i.save(using='slave')
ContentType.objects.using('slave').all().delete()
for i in ContentType.objects.all():
do(i.model_class())
如果有任何类型的``signals``,需要先禁用它。
5.同步
进入shell
python ./manage.py shell
在python shell中执行
from to_slave import run
run()
6.将``slave``变为默认的数据库。。
Django Tastypie 构建Django RESTful API2014-05-17T01:23:42+00:002015-08-07T05:15:25.433778+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/django-tastypie-to-extend-mezzanine-cms-api/当我还在纠结是否继续用``Nodejs``构建博客API的时候(可以参考下[重构个人网站的畅想](http://www.phodal.com/blog/refactor-person-homepage-think/)),想起当时研究``RESTful``的时候,除了Laravel RESTful([创建RESTful](http://www.phodal.com/blog/bare-minimum-iot-system-create-restful/),[详解Laravel的RESTful](http://www.phodal.com/blog/bare-minimum-iot-system-about-restful/)),最先用的就是Django-REST-Framedowk。于是,想起当时没有用到的其他框架,也就是``Tastypie``。
#Tastypie
##Tastypie简介
> Tastypie is a webservice API framework for Django. It provides a convenient, yet powerful and highly customizable, abstraction for creating REST-style interfaces.
中文简介版
> Tastypie是Django框架的一个webservice接口。它提供了一个方便、强大而且高定制化的REST风格接口。
> Tastypie非常容易显示您的模型,但也能完全让你控制你所期望得到的信息。只要你需要,能让你远离抽象的数据库。Tastypie也能让你非常容易的集成非ORM的数据源。
##Tastypie VS Nodejs RESTify
似乎用RESTify没有什么不好的,但是缺点是``我们离抽象数据库很近``。
详细的内容可以看这篇[ nodejs restify sqlite3网站重构二,生成RESTful接口](http://www.phodal.com/blog/restify-sqlite3-nodejs-to-refactory-website/)
##Tastypie 安装
1.pip的话自然就是
sudo pip instal django-tastypie
或者
sudo easy_install django-tastypie
2.添加到``settings.py``中的``INSTALLED_APPS``
'tastypie',
3.同步数据库
sudo manage.py syncdb
#Tastypie Django CMS
由于我使用的是``Mezzanine``的CMS,相比于自己动手写的优点是:
<div class="posts">
- 自动生成的sitemap.xml
- 已经集成博客系统
- 可以方便地添加page
- 添加模板很方便
</div>
当年的Django CMS没有这么强大,就这样已经用了差不多两年了。、
##Tastypie实战
最后程序的目录结构如下所示
blogapi/
├── __init__.py
├── __init__.pyc
├── api.py
└── api.pyc
主要的也就是``__init__.py``和``__api__.py``,有意思的是``__init__.py``很早以前在写``Django 扩展``的时候没有意识到这个,当时死活不工作,这是一个``空文件``。
###配置Resource
修改urls.py,添加下面的内容
from tastypie.api import Api
from blogapi.api import AllBlogSlugResource,EntryResource
v1_api = Api(api_name='v1')
v1_api.register(AllBlogSlugResource())
v1_api.register(EntryResource())
urlpatterns += patterns('',
(r"^api/", include(v1_api.urls)),
)
或者直接添加到博客的URL上方,如官方的示例
# urls.py
from django.conf.urls.defaults import *
from myapp.api import EntryResource
entry_resource = EntryResource()
urlpatterns = patterns('',
# The normal jazz here...
(r'^blog/', include('myapp.urls')),
(r'^api/', include(entry_resource.urls)),
)
###创建Resource
一个简单的示例代码如下如下,大致就是从``mezzanine``中读出包括``keywords_string``,``slug``,``title``的内容,资源的名字是``url``。
第二个则是构建博客的内容,这样我们就可以同上次一样,构建出一个可以调用的API。
from tastypie.resources import ModelResource
from mezzanine.blog.models import BlogPost, BlogCategory
class AllBlogSlugResource(ModelResource):
class Meta:
queryset = BlogPost.objects.published()
resource_name = "url"
fields = ['keywords_string', 'slug', 'title']
allowed_methods = ['get']
class EntryResource(ModelResource):
class Meta:
queryset = BlogPost.objects.published()
resource_name = "blog"
fields = ['keywords_string', 'slug', 'title', 'content', 'description']
allowed_methods = ['get']
###测试Resource
用于获取url的json结果
curl -H "Accept: application/json" http://127.0.0.1:8000/api/v1/url/
用于获取blog的json结果
curl -H "Accept: application/json" http://127.0.0.1:8000/api/v1/blog/Python2.7 virtualenv 部署Mezzanine Django CMS2014-05-16T13:15:26+00:002014-05-16T13:31:26.799636+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/use-virtualenv-python27-deploy-mezzanine-gunicorn-newrelic/在我意识到所谓的博客问题的主要来源可能是来自于Python版本的时候,才理解为什么可以在本地运行得很好,而在服务器上是有问题的。
##Virtualenv Python2.7
在CentOS,其他系统应该是类似的
virtualenv -p /usr/local/bin/python2.7 app
于是我们就可以这样创建一个开发环境,或者说是运行环境,对于像``CentOS``这样有Python版本问题的系统来说。(YUM需要python2.6)
Mac OS下如果是用``HomeBrew``安装可能会是
virtualenv -p /opt/local/bin/python2.7 app
##Mezzanine Virtualenv
开始之前需要先切换到``virutalenv``环境
可以直接用
source app/bin/activate
查看一下
<pre><code class="bash">
[root@CentOS62 MK_dream]# source env/bin/activate
(env)[root@CentOS62 MK_dream]# which pip
/home/www/MK_dream/env/bin/pip
</code></pre>
可以看到``pip``的命令在这时是默认在目录下
###安装依赖
接着我们安装依赖
pip install -r requirements.txt
不过最简单的还是可以直接用
pip install mezzanine
###安装运行环境
1.安装Gunicorn
pip install gunicorn
2.安装newrelic
pip install newrelic
不知道``newrelic``是什么可以参加一下
[New Relic网站分析 apdex](http://www.phodal.com/blog/use-apdex-with-new-relic-analyse-website/)
[New Relic Django 配置,用New Relic监控应用状态
](http://www.phodal.com/blog/new-relic-django-mezzanine-guniconr-settings/)
###运行
先测试一下是否可以运行
gunicorn_django --workers=2 -b 127.0.0.1:8080 --timeout=300
编写一个简单的运行脚本
<pre><code class="bash">
killall gunicorn_django
NEW_RELIC_CONFIG_FILE=newrelic.ini
export NEW_RELIC_CONFIG_FILE
nohup /home/www/MK_dream/env/bin/newrelic-admin run-program /home/www/MK_dream/env/bin/gunicorn_django --workers=2 -b 127.0.0.1:8000 --timeout=300&
</code></pre>
果然当遇到版本升级的时候就是一个坑,似乎也因此变得越来越专业了。
django debug toolbar 安装2014-05-15T01:10:02+00:002015-06-15T12:35:41.285896+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/django-install-django-debug-toolbar/当我继续纠结于网站的性能的时候,我开始去寻找一切的办法,包括向作者寻问。最后作者告诉我可以试试用``Django Debug Toolbar``。
##Django Debug Toolbar简介
> The Django Debug Toolbar is a configurable set of panels that display various debug information about the current request/response and when clicked, display more details about the panel's content.
``Django Debug Toolbar``D是一个可配置的组显示有关当前请求/响应和点击时的各种调试信息,显示有关面板的内容的更多详细信息面板。
看看官网截图
[Django Debug Toolbar](https://raw.github.com/django-debug-toolbar/django-debug-toolbar/master/example/django-debug-toolbar.png)
看上去似乎真的如传说中的一样强大,只是安装有点麻烦。。
##Django Debug Toolbar安装
1.安装Django Debug Toolbar
sudo pip install django-debug-toolbar
2.添加到``INSTALLED_APPS``,添加
"debug_toolbar",
3.添加下行到``MIDDLEWARE_CLASSES``,建议放到最后一行
'debug_toolbar.middleware.DebugToolbarMiddleware',
4.``INTERNAL_IPS``设置,如果你和我一样已经放在服务器上了,就加上你的IP。
INTERNAL_IPS = ('127.0.0.1',)
5.不自动调整设置
DEBUG_TOOLBAR_PATCH_SETTINGS = False
6.添加到``URLconf``
<pre><code class="python">
from django.conf import settings
from django.conf.urls import include, patterns, url
if settings.DEBUG:
import debug_toolbar
urlpatterns += patterns('',
url(r'^__debug__/', include(debug_toolbar.urls)),
)
</code></pre>
7.自定义工具栏
<pre><code class="python">
DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.versions.VersionsPanel',
'debug_toolbar.panels.timer.TimerPanel',
'debug_toolbar.panels.settings.SettingsPanel',
'debug_toolbar.panels.headers.HeadersPanel',
'debug_toolbar.panels.request.RequestPanel',
'debug_toolbar.panels.sql.SQLPanel',
'debug_toolbar.panels.staticfiles.StaticFilesPanel',
'debug_toolbar.panels.templates.TemplatesPanel',
'debug_toolbar.panels.cache.CachePanel',
'debug_toolbar.panels.signals.SignalsPanel',
'debug_toolbar.panels.logging.LoggingPanel',
'debug_toolbar.panels.redirects.RedirectsPanel',
]
</code></pre>
最后效果如下所示
![Debug Tool bar local][1]
[1]: /static/media/uploads/django-tool-bar.jpgMezzanine 添加页面2014-05-14T12:59:23+00:002014-08-03T14:42:45.660683+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/mezzanine-add-new-page/想着想着突然需要给博客添加一个新的页面,也就是之前说的[全部文章](http://www.phodal.com/all/),虽然这是一个测试功能的东西。。
#Mezzanine 添加页面
``Mezzanine``添加页面,大致上有点和``Django``类似,不同的是加的地方不一样。
1.将html文件放到templates目录,这里以``all.html``为例,放在``templates/pages/``下面。
2.编辑``urls.py``添加
url("^all/$", direct_to_template, {"template": "pages/all.html"},name="all"),
3.在``Pages``页面添加一个新的``link``,URL名字应该和``urls.py``中的all一样。
重启一下应用,这样就可以添加一个新的页面。
New Relic Django 配置,用New Relic监控应用状态2014-05-13T12:24:25+00:002015-04-01T11:12:14.965435+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/new-relic-django-mezzanine-guniconr-settings/使用New Relic后才算知道网站真正的问题出在哪里。
#New Relic简介
官网是这么介绍的
> Offers a performance management solution enabling developers to diagnose and fix application performance problems in real time.
一个中文的说明。。
> New Relic公司的性能工具叫做RPM,它是一种提供给公司的Saas解决方案,可以提供性能监视和分析服务。能够对部署在本地或在云中的web应用程序进行监控、故障修复、诊断、线程分析以及容量计划。
##开始之前
从官网注册一个帐号,然后获取相应的``API key``。
#Django New Relic
##安装Newrelic Python库
直接用pip
sudo pip install newrelic
又或者是``easy_install``
sudo easy_install newrelic
##Django New Relic配置
1.生成配置文件。(需要用到注册的``key``)。
newrelic-admin generate-config LICENSE-KEY newrelic.ini
2.如果需要log文件,编辑``newrelic.ini``,将下行的注释去掉
log_file = /tmp/newrelic-python-agent.log
3.修改``app name``
app_name = Python Application
4.测试
newrelic-admin validate-config newrelic.ini
##Django New Relic Gunicorn
由于博客用到的是``Gunicorn``+``Nginx``+``Django``,所以相应的启动脚本变成了
<pre><code class="bash">
NEW_RELIC_CONFIG_FILE=newrelic.ini
export NEW_RELIC_CONFIG_FILE
nohup newrelic-admin run-program gunicorn_django --workers=2 -b 127.0.0.1:8088 --timeout=300&
</code></pre>
类似的有
newrelic-admin run-program python manage.py run_gunicorn -b "0.0.0.0:$PORT" -w 3Mezzanine 1.3 升级 Mezzanine 3.0——一场噩梦2014-01-14T17:17:44+00:002014-05-13T13:51:04.538589+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/mezzanine-1.3-upgrade-mezzanine-3.0/##升级##
###理论方法###
pip install mezzanine --upgrade
sudo python manage.py syncdb
sudo python manage.py migrate
###其他需要考虑的几个问题###
1. Blogpost
2. Blog Keyword
3. Configure Setting
###附带需要考虑的问题###
1. SEO
2. 404
3. Posts Blog
##数据库##
###查看指定表###
sudo python manage.py sql blog
这个是原来的blog结构
BEGIN;
CREATE TABLE `blog_blogpost_related_posts` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`from_blogpost_id` integer NOT NULL,
`to_blogpost_id` integer NOT NULL,
UNIQUE (`from_blogpost_id`, `to_blogpost_id`)
)
;
CREATE TABLE `blog_blogpost_categories` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`blogpost_id` integer NOT NULL,
`blogcategory_id` integer NOT NULL,
UNIQUE (`blogpost_id`, `blogcategory_id`)
)
;
CREATE TABLE `blog_blogpost` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`comments_count` integer NOT NULL,
`keywords_string` varchar(500) NOT NULL,
`rating_count` integer NOT NULL,
`rating_sum` integer NOT NULL,
`rating_average` double precision NOT NULL,
`site_id` integer NOT NULL,
`title` varchar(500) NOT NULL,
`slug` varchar(2000),
`_meta_title` varchar(500),
`description` longtext NOT NULL,
`gen_description` bool NOT NULL,
`created` datetime,
`updated` datetime,
`status` integer NOT NULL,
`publish_date` datetime,
`expiry_date` datetime,
`short_url` varchar(200),
`in_sitemap` bool NOT NULL,
`content` longtext NOT NULL,
`user_id` integer NOT NULL,
`allow_comments` bool NOT NULL,
`featured_image` varchar(255)
)
;
ALTER TABLE `blog_blogpost` ADD CONSTRAINT `site_id_refs_id_ac21095f` FOREIGN KEY (`site_id`) REFERENCES `django_site` (`id`);
ALTER TABLE `blog_blogpost` ADD CONSTRAINT `user_id_refs_id_01a962b8` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
ALTER TABLE `blog_blogpost_related_posts` ADD CONSTRAINT `from_blogpost_id_refs_id_6404941b` FOREIGN KEY (`from_blogpost_id`) REFERENCES `blog_blogpost` (`id`);
ALTER TABLE `blog_blogpost_related_posts` ADD CONSTRAINT `to_blogpost_id_refs_id_6404941b` FOREIGN KEY (`to_blogpost_id`) REFERENCES `blog_blogpost` (`id`);
ALTER TABLE `blog_blogpost_categories` ADD CONSTRAINT `blogpost_id_refs_id_6a2ad936` FOREIGN KEY (`blogpost_id`) REFERENCES `blog_blogpost` (`id`);
CREATE TABLE `blog_blogcategory` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`site_id` integer NOT NULL,
`title` varchar(500) NOT NULL,
`slug` varchar(2000)
)
;
ALTER TABLE `blog_blogcategory` ADD CONSTRAINT `site_id_refs_id_93afc60f` FOREIGN KEY (`site_id`) REFERENCES `django_site` (`id`);
ALTER TABLE `blog_blogpost_categories` ADD CONSTRAINT `blogcategory_id_refs_id_91693b1c` FOREIGN KEY (`blogcategory_id`) REFERENCES `blog_blogcategory` (`id`);
COMMIT;
###新的blog###
BEGIN;
CREATE TABLE "blog_blogpost_related_posts" (
"id" integer NOT NULL PRIMARY KEY,
"from_blogpost_id" integer NOT NULL,
"to_blogpost_id" integer NOT NULL,
UNIQUE ("from_blogpost_id", "to_blogpost_id")
)
;
CREATE TABLE "blog_blogpost_categories" (
"id" integer NOT NULL PRIMARY KEY,
"blogpost_id" integer NOT NULL,
"blogcategory_id" integer NOT NULL,
UNIQUE ("blogpost_id", "blogcategory_id")
)
;
CREATE TABLE "blog_blogpost" (
"id" integer NOT NULL PRIMARY KEY,
"comments_count" integer NOT NULL,
"keywords_string" varchar(500) NOT NULL,
"rating_count" integer NOT NULL,
"rating_sum" integer NOT NULL,
"rating_average" real NOT NULL,
"site_id" integer NOT NULL REFERENCES "django_site" ("id"),
"title" varchar(500) NOT NULL,
"slug" varchar(2000),
"_meta_title" varchar(500),
"description" text NOT NULL,
"gen_description" bool NOT NULL,
"created" datetime,
"updated" datetime,
"status" integer NOT NULL,
"publish_date" datetime,
"expiry_date" datetime,
"short_url" varchar(200),
"in_sitemap" bool NOT NULL,
"content" text NOT NULL,
"user_id" integer NOT NULL REFERENCES "auth_user" ("id"),
"allow_comments" bool NOT NULL,
"featured_image" varchar(255)
)
;
CREATE TABLE "blog_blogcategory" (
"id" integer NOT NULL PRIMARY KEY,
"site_id" integer NOT NULL REFERENCES "django_site" ("id"),
"title" varchar(500) NOT NULL,
"slug" varchar(2000)
)
;
COMMIT;
##迁移##
###数据问题###
1.Blog标签问题
删除的内容
{% keywords_for blog.blogpost as tags %}
路径
/home/www/MK_phodal/templates/blog/includes/change_list.html
2. pytz问题
删除的内容
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
路径
/usr/lib/python2.6/site-packages/grappelli_safe/templates/admin/filter_panel.htmlMezzanine Django Linux Nginx快速python CMS部署2013-02-28T20:28:08+00:002014-05-16T09:55:25.567477+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/Mezzanine-Django-Linux-Nginx/<p>
</p>
<p>
开始之前了解一下Mezzanine是什么:
</p>
<p>
</p>
> Mezzanine is a powerful, consistent, and flexible content management platform. Built using the <a class="reference external" href="http://djangoproject.com/">Django</a> framework, Mezzanine provides a simple yet highly extensible architecture that encourages diving in and hacking on the code. Mezzanine is <a class="reference external" href="http://www.linfo.org/bsdlicense.html">BSD licensed</a> and supported by a diverse and active community.
> In some ways, Mezzanine resembles tools such as <a class="reference external" href="http://wordpress.org/">Wordpress</a> that provide an intuitive interface for managing pages, blog posts, form data, store products, and other types of content. But Mezzanine is also different. Unlike many other platforms that make extensive use of modules or reusable applications, Mezzanine provides most of its functionality by default. This approach yields a more integrated and efficient platform.
<p>
这也就是官方的简介,简单地说Mezzanine就是一个基于Django框架的应用,同时他提供了类似于wordpress的功能。换句话说,Mezzanine就是一个wordpress,我们只需要简单的修改就可以部署了。一个新项目创建时的截图,
</p>
<p>
<img alt="" src="http://my.csdn.net/uploads/201208/03/1343961458_9781.png"/><br/>
</p>
<p>
我们可以看到Mezzanine用作商业的时候是如此的简单明了。
</p>
<p>
详细可以参考官方网站:<a href="http://mezzanine.jupo.org/">http://mezzanine.jupo.org/</a>
</p>
<p>
Mezzanine快速指引
</p>
<p>
</p>
<pre><code class="bash"># Install from PyPI
$ pip install mezzanine
# Create a project
$ mezzanine-project myproject
$ cd myproject
# Create a database
$ python manage.py createdb
# Run the web server
$ python manage.py runserver
</code></pre>
<br/>
这也就完成了本地开发的第一部。我们可以发现,新建的项目里面我们很难修改主题,如下所示
<p>
</p>
<p>
</p>
<pre><code class="bash">3240 ./static/media/uploads/gallery
3244 ./static/media/uploads
3248 ./static/media
3252 ./static
24 ./deploy
8 ./requirements
12 ./templates
3520 .
</code></pre>
<br/>
<p>
可以使用
</p>
<p>
</p>
<pre><code class="bash">python manage.py collecttemplates --help</code></pre>
<br/>
<p>
</p>
<p>
收集templates
</p>
<p>
因而,我们需要另外的文件,也就是templates,这个没有在需要额外配置。
</p>
<p>
<br/>
</p>
<p>
</p>
<p>
</p>
<pre class="html" name="code">git clone https://github.com/renyi/mezzanine-themes.git</pre>
<br/>
然后复制目录中<span>的mazzanine_themes/mazzanine_default到templates中,这样就可以修改默认的样式了。</span>
<p>
</p>
<p>
<span>关于部署由于之前静态文件的设置问题,因此也就贴了出来。换句话说,默认的静态文件和Django一样需要修改网站nginx的配置文件,比如我的是www.phodal.com.conf。</span>
</p>
<p>
<span> </span>
</p>
<pre><code class="bash">location /static {
autoindex on;
alias /home/gmszone/Phodal/static;
access_log off;
log_not_found off;
}
location /robots.txt
alias /home/gmszone/Phodal/static;
access_log off;
log_not_found off;
}
location /favicon.ico {
alias /home/gmszone/Phodal/static/img;
access_log off;
log_not_found off;
}
</code></pre>
<span><br/>
</span>
<p>
</p>
<p>
<span>也就是要由nginx指定static的位置,也就没有那么多,只需要。记得重启一下nginx</span>
</p>
<p>
<span></span>
</p>
<pre class="html" name="code"><span>alias /home/gmszone/Phodal/static;</span></pre>
<span><br/>
至于部署方式可以采用,uWSGI。</span>
<p>
</p>
<p>
<span>安装完uWSGI需要,两个文件以便使之运作。</span>
</p>
<p>
<span></span>
</p>
<pre><code class="python">import os,sys
if not os.path.dirname(__file__) in sys.path[:1]:
sys.path.insert(0, os.path.dirname(__file__))
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()
</code></pre>
<span><br/>
也就wsgi.py,以及</span>
<p>
</p>
<p>
<span></span>
</p>
<pre class="html" name="code"><span><uwsgi>
<socket>127.0.0.1:8630</socket>
<chdir>/home/gmszone/Phodal</chdir>
<pythonpath>..</pythonpath>
<module>wsgi</module>
</uwsgi></span></pre>
<span><br/>
最后再运行,</span>
<p>
</p>
<p>
<span></span>
</p>
<pre class="html" name="code"><span>uwsgi -x /home/gmszone/Phodal/wsgi.xml</span></pre>
<span><br/>
</span>
<p>
<span>详细可以参照:<a href="http://projects.unbit.it/uwsgi/wiki/Example">http://projects.unbit.it/uwsgi/wiki/Example</a></span>
</p>
<p>
<br/>
</p>
<p>
</p>
<p>
</p>