@Lenciel

Correct Django Site Name During DB Migration

Don't touch me

Problem

就像截图上显示的那样,真正上线过的 Django 项目都会好像被施放过诅咒一般,让你在某一天看到那个诡异的example.com

它可能是在系统发出去的重置密码的邮件里面,可能是在 Sentry 显示的日志里面,也可能就在你用 site_name tag 渲染的模板里面。

这个诅咒来自于 Django 的sites framework的设计。简单来说,它提供了一个 Site 对象的manager,来方便你用一套代码给多个部署环境使用。换句话说,虽然settings.py文件里面也有一个SITE_NAME,但其实用Site.objects.get_current().name或者是模板里面的site_name取到的不是那个值,而是数据库django_site里面某个site_id对应的 Site 对象的name

而如果你syncdb之后没有手工修改过,Sitedomainname都被默认初始化为example.com,这就是问题所在了。

Solution

stackoverflow 上得票最高的答案这样把site_name放到responselocal()里面或者是直接做个context_processor是可以的。但这样的坏处是完全抛弃了 Django 自带的sites,需要在用的地方都专门的处理。

如果要继续使用自带的sites,就得自己写类似下面的 fixture:

[
  {
    "pk": 1,
    "model": "sites.site",
    "fields": {
      "name": "LeiFun Production",
      "domain": "leifun.net"
    }
  },
  {
    "pk": 2,
    "model": "sites.site",
    "fields": {
      "name": "LeiFun Stage",
      "domain": "stage.leifun.net"
    }
  },
  {
    "pk": 3,
    "model": "sites.site",
    "fields": {
      "name": "LeiFun Test",
      "domain": "test.leifun.net"
    }
  },

  {
    "pk": 4,
    "model": "sites.site",
    "fields": {
      "name": "LeiFun Local Dev",
      "domain": "yawp.dev:8000"
    }
  }
]

然后在部署的环境里面用django_admin.py或者manage.py运行loaddata。这样的坏处是fixture这东西本来主要是给本地测试生成 mock 数据的,所以syncdb命令其实不会发起 fixture 的导入,于是很多时候你部署了新版本之后,会忘记重新导入fixture(其实本来也不该导入 fixture),牛皮癣一样的example.com又回来了。

Solution 2

通过修改某个现成 app 的Migration类的forwards方法,强制它读取一次settings文件里面的配置项:

class Migration(DataMigration):

    def forwards(self, orm):
        Site = orm['sites.Site']
        site = Site.objects.get(id=settings.SITE_ID)
        site.domain = settings.DOMAIN_NAME
        site.name = settings.SITE_NAME
        site.save()

这样一来,就可以在syncdb的时候刷新django_site这张表的配置。

Solution Finally

在 Django 1.7 里面,这个倒霉的设计终于被改掉了

To enable the sites framework, follow these steps:

1. Add 'django.contrib.sites' to your INSTALLED_APPS setting.
2. Define a SITE_ID setting
3. Run migrate.

django.contrib.sites registers a post_migrate signal handler which creates a default site named example.com with the domain example.com. This site will also be created after Django creates the test database. To set the correct name and domain for your project, you can use a data migration.

不但如此,Django 1.7 还引入了django.contrib.sites.middleware.CurrentSiteMiddleware, 如果启用,就可以直接使用request.site而不需要在你的view里面自己去调用site = Site.objects.get_current()了。

聚散有期

上周参加了偶像派她爷爷的 90 大寿,这周就迎来了她另一位爷爷离世的消息。

这位爷爷是我丈母娘保娘(干妈)的老伴,在一场事后被认为是回光返照的愉快聊天后,他独自在卧室睡去,再也没有醒来。

在睡梦中安静的走掉,再加上 93 岁的高寿,所以我们当然会互相安慰说,不用太伤心,这是寿终正寝。

但一讲起还没有走的婆婆,大家又会忍不住担心起来。

爷爷从不做农活,也不做家务,每天就喝茶打牌养养鸽子。几十年这么被惯着,不但离了婆婆连饭都不会做,甚至连面都不会下。我没有问过他选择这样生活的原因:也许是当年参加抗美援朝,在那片遥远的冰天雪地耗尽了所有的力气吧。

他们也一直没有孩子。

我们每年会去很多次他们在敖平乡下的家,给他们带些粮油和现金。

每次婆婆知道我们要去,就提前到地里掐好自己种的菜,捡好自己养的鸡下的蛋。等我们到了,就一直在厨房里忙活。有时候天气好,我们就把桌子搬到屋外的院子里面,每个人倒一点儿酒慢慢的喝着边吃边等她在厨房忙完上桌。

那时候她已经八十多岁了。

因为牙齿不好,胃口也开始变差。所以大多数时候,她只不过象征性的吃一点儿,就点上一根烟,带着满足的笑容坐在一旁听我们聊天。她很少说话,听到开心的地方,就拿起酒杯微微地抿一口。

在汶川地震之后,他们村里的人被集中安置居住时,他们又选择了留守。四周的邻居逐渐搬走,岳父岳母也去劝过他们和大家一起搬走好有个照应,但他们拒绝了。

更不要说搬来和我们一起住。

据说理由是在自己地里忙活了一辈子,何必要在别的地方去死。

是啊,人到了一定年纪,在哪里死去终于变成一件迫在眉睫的事情。

不知道从哪次去的时候开始,婆婆就已经忙活不动了。大家好像也没有商量过什么,就默默变成我们开车带他们去镇上的饭馆吃一顿,然后送他们回去。

这过程自然比以前要快了不少,每次告别,我都能看出婆婆眼中的不舍:她是个特别重感情的人。

爷爷走了,她反复告诉岳母:「把那些钱拿去好好把事情办了,剩下几千块钱留给我就行了。」

「反正我十一之后就走了」,她这么说。

大家都不停劝她想开一些,但我们都知道她的倔强。

就好像她本来身体比爷爷要差,但一直倔强地撑着,大概她知道自己走了就没人能像自己一样照顾他。

坦白说,他们的感情既让我敬佩,也常常让我疑惑。

有时候我看着婆婆,会想,她有没有偶尔也后悔过没有一个完整的家庭?和爷爷这样的男人厮守到老,是因为依恋,犹豫,认命,还是别的什么东西?

但仔细想想,可能也没有需要特别努力的地方。

反正人生里美好的不过是一些片断:在某个午后踢球赢了比自己更强的对手,夏天很热的时候吃到冰箱里拿出的熟得正好的西瓜,放肆地看着暗恋的对象她/他也正好看着你,跟相爱的人一起度过没人打扰的夜晚,甚至,没有充满恐惧而是在睡梦中平静死去也是美好的。

能抓住它们就好,别的东西我们自己大概很难掌握。

这也是人类到了一定年纪就拒绝去相信纯粹的东西可以持久的原因。毕竟我们这一生会丢失很多东西:首先是理想主义,然后是激情,接下来是容貌和身材,最后是有趣味的生活。想要保持点什么贯穿始终的东西如此不易,倒不如先否认这些需要认真照看的东西,可以让自己生活得容易一些,死的时候也不会太艰难。

记性太好也是需要移除的错误天赋。就好比记得越牢的号码,变成「您拨打的电话无人接听」的那天就越不堪,还不如记错或者干脆忘得一干二净。

也可以给大家都想个理由,就好像张爱玲写她等胡兰成:「雨声潺潺,像住在溪边。宁愿天天下雨,以为你是因为下雨不来。」

该忘记的忘记,剩下的也糊涂些为好。最好还能幽默一些,强势一些,牙尖嘴利地把自己包裹起来生人勿近。

就好像也在这个月刚刚过世的 Joan Rivers 说:”My sex life is so bad, my G-spot has been declared a historical landmark.”

反正,这世界聚散有期,又有几个人在意这些牙尖嘴利的人真正的心思呢。