用Django&Heroku快速迭代
开展一项在线业务可能会被证明是非常复杂的。虽然在纸面上,创建一家在线企业要比实体企业容易得多,但一个企业家可能会迷失在多种选择之中。在线企业家遇到的一些最常见的陷阱包括:
- 建得太早::浪费时间和金钱建立一个复杂的产品。一路上失去工作积极性,对产品失去信心,放弃项目。
- 过于相信这个想法:坚持原来的想法,不重复它,即使客户不出现,不付钱,或不满意。
- 未启动::当一个人沿着构建网络项目的道路开始,他/她可能会被看似无限的决策和选择所淹没。使用什么主机?什么站台?什么WordPress主题?如何建立一个高转换登陆页?什么编程语言和什么数据库?您应该使用Web框架吗?用于前端的香草JavaScript还是jQuery?也许是一个更复杂的前端框架,因为一旦项目足够成熟,它就需要一个?
- 未能发射:当构建一个网络项目时,即使你决定了你的技术栈,你也会被你得到的反馈所淹没。相反,听太多的反馈被认为是错误的。这可能是来自人们的反馈,无论如何也不会使用你的产品。人们往往对任何事情都有自己的看法,尽管他们对这一领域并不完全了解。
考虑到一个人在路上可能会失败的方式很多,所以真正重要的是:
- 越少越好,越快越好,把它展示给你认为是潜在客户的人。*尽量减少成本和努力。
- 尽快把它放到网上:从人们那里得到对产品的反馈,而不是对你抽象的想法的反馈。
- 迅速改变当了解客户想要什么时,关键是要保持敏捷,并很好地为您的第一批付费客户服务。
这里是原型制作的地方。企业家应该精打细算,而不是浪费时间和资源。在一开始尽可能少的建筑可以证明是一种美德。
关于原型是什么以及应该如何创造,有很多种思想流派。有人说它应该只是一个登陆页,另一些人则认为它应该是一个精简版的最终产品。我更喜欢第二个。只使用登陆页就能感觉到欺骗。而且,你不能得到关于你是如何解决问题的反馈,而只是关于这个问题是否值得解决。
以下是智能原型在线企业家的工具箱:
- 前端框架:引导、基础、jQuery、Vue等。使用前端框架可以使应用程序在不同的屏幕大小和不同的浏览器上工作,并具有良好的设计。
- 后端框架::Django,Ruby on Rails,Laravel使用后端框架可以帮助您轻松处理HTML模板、HTTP表单、数据库访问、URL模式等。
- 服务平台::Heroku,谷歌应用引擎,AWS弹性豆柄。选择PaaS可以使您摆脱管理服务器、日志聚合、正常运行时间监视、部署基础设施等方面的痛苦。
在本教程中,我们将本着快速原型的精神构建一个简单的应用程序。我们将使用Django、Bootstrap CSS和Heroku。重点将在后端部分,而不是前端。
我们将利用Heroku平台,尽早将一些内容放到网上,并快速部署新功能。我们将使用Django来构建复杂的数据库模型和功能。引导CSS将为我们的页面提供一个合理的默认样式。说够了,我们走。
我们在建造什么
一定要坐下来看这个。这个主意会让你大吃一惊的。这里有一个问题:难道你不讨厌如何得到所有这些折扣代码,但你忘记使用它们,它们就过期了吗?
把密码存储在一个可以搜索它们的地方,并且在它们即将过期的时候得到通知,这不是很酷吗?我知道,好主意,对吧?好吧,把你的信用卡放下,你就不会在这张上投资了。你要建造它。
开始
在本教程中,我将使用Python 3,如果您使用的是Python2.7,则更改应该相当容易。我也会假设你很熟悉setuptools
、Python Virtualenv和Git。在前进之前还有一件事:确保你有一个GitHub和Heroku帐户。要使用Heroku,还需要安装Heroku CLI.
让我们首先创建一个虚拟环境:
|
1
|
$ mkvirtualenv coupy
|
您可能已经知道,我们的应用程序名是库皮...让我们切换到新的虚拟环境,$ workon coupy
,并安装Django:
|
1
|
$ pip ``install
Django
|
进入您的GitHub帐户并创建一个新项目。接下来,让我们克隆这个项目:
|
1
2
|
$ git clone https:``//github``.com/<GITHUB_USERNAME>/<GITHUB_PROJECT_NAME>.git
$ ``cd
<GITHUB_PROJECT_NAME>
|
下一个逻辑步骤是创建Django项目。要将Django项目部署到Heroku,我们需要遵循一些指导原则。幸运的是,我们可以使用一个项目模板。以下是如何做到这一点:
|
1
|
$ django-admin.py startproject --template=https:``//github``.com``/heroku/heroku-django-template/archive/master``.zip --name=Procfile coupy
|
你可能需要移动一些文件夹。确保存储库根文件夹如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
.
├── Procfile
├── README.md
├── coupy
│ ├── __init__.py
│ ├── settings.py
│ ├── static
│ │ └── humans.txt
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── requirements.txt
└── runtime.txt
|
现在让我们安装模板提供的需求:
|
1
|
$ pip ``install
-r requirements.txt
|
现在,我们希望将新创建的文件推送到GitHub:
|
1
2
3
|
$ git add .
$ git commit -m``"Init Django project"
$ git push origin master
|
让我们看看我们到目前为止所做的工作是否有效:
|
1
|
$ python manage.py runserver
|
现在打开一个浏览器窗口,然后转到http://localhost:8000...如果一切都很好,您应该会看到经典的Django欢迎页面。为了确保从Heroku的角度来看一切都是好的,我们还可以像这样运行这个应用程序:
|
1
|
$ heroku ``local
web
|
为了证明我们能以多快的速度上网,让我们第一次部署到Heroku:
|
1
2
|
$ heroku login
$ heroku create
|
我们现在已经创建了Heroku应用程序,但是我们没有向Heroku发送任何代码。注意,Heroku创建了一个用户友好的应用程序id。下面是您应该得到的输出:
|
1
2
|
Creating app... ``done``, ⬢ <HEROKU_APP_ID>
https:``//``<HEROKU_APP_ID>.herokuapp.com/ | https:``//git``.heroku.com/<HEROKU_APP_ID>.git
|
我们现在需要将我们的回购与新创建的Heroku应用程序联系起来:
|
1
2
3
|
$ heroku git:remote -a <HEROKU_APP_ID>
$ git push heroku master
$ heroku ``open
|
太棒了,你刚刚在Heroku部署了一个应用程序。它不起什么作用,但是你把一些东西放在了创纪录的时间里。做得好。
广告
建立数据库
如果没有数据库,你可能永远不会构建一个不平凡的web应用程序。数据库是Web应用程序的数据存储部分。这是web应用程序保持状态的地方(至少大部分是这样的)。这里是我们保存用户帐户和登录细节的地方,还有更多,更多。Heroku提供托管PostgreSQL服务。
这就是我们要用的。确保您已经在计算机上安装了Postgres,并创建一个在我们的应用程序中使用的数据库实例。Heroku需要设置一个环境变量,以便能够连接到数据库服务。我们需要设置的变量是DATABASE_URL
:
|
1
|
$ ``export
DATABASE_URL=``"[postgres://<USERNAME>:<PASSWORD>@localhost:5432/<DATABASE_NAME](postgres://<USERNAME>:<PASSWORD>@localhost:5432/<DATABASE_NAME)>"
|
现在让我们告诉Django应用迁移并创建必要的表:
|
1
|
$ .``/manage``.py migrate
|
让我们创建一个超级用户并登录到管理界面http://localhost:8000/admin:
|
1
2
|
$ .``/manage``.py createsuperuser
$ .``/manage``.py runserver
|
我们可以看到,这些表确实已经创建。默认情况下,Heroku已经将数据库实例与您的应用程序关联起来。你可以通过签入Heroku来确保这是真的HEROKU_APP_ID > Settings > Config Variables
在你的Heroku在线控制台。你应该在这里看到DATABASE_URL
设置为Heroku生成的数据库地址。
现在我们必须运行迁移并在线创建超级用户命令。让我们看看是否一切都如预期的那样工作:
|
1
2
|
$ heroku run python manage.py migrate
$ heroku run python manage.py createsuperuser
|
如果一切顺利,如果我们去https://<HEROKU_APP_ID>.herokuapp.com/admin/
,我们应该能够登录我们刚才提供的凭据。
用户认证
在本节中,我们将初始化Django应用程序,并使用Django预定义组件在应用程序中创建用户身份验证功能。
|
1
|
$ .``/manage``.py startapp main
|
在新应用程序中,我们将创建一个urls.py
档案:
|
01
02
03
04
05
06
07
08
09
10
|
from
django.conf.urls ``import
url
from
django.contrib.auth ``import
views as auth_views
from
django.views.generic.base ``import
RedirectView
urlpatterns ``=
[
url(``'^$'``, RedirectView.as_view(url``=``'login'``), name``=``'index'``),
url(r``'^login$'``, auth_views.LoginView.as_view(), name``=``'login'``),
url(r``'^logout$'``, auth_views.LogoutView.as_view(), name``=``'logout'``),
]
|
这里我们使用三个通用Django视图:
-
RedirectView
:因为应用程序的基本URL没有做任何事情,所以我们要重定向到登录页面。 -
LoginView
:Django预定义视图,该视图创建登录表单并实现用户身份验证例程。 -
LogoutView
:Django预定义视图,该视图记录用户并重定向到某个页面。
添加main
向INSTALLED_APPS
清单:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
INSTALLED_APPS ``=
[
'django.contrib.admin'``,
'django.contrib.auth'``,
'django.contrib.contenttypes'``,
'django.contrib.sessions'``,
'django.contrib.messages'``,
# Disable Django's own staticfiles handling in favour of WhiteNoise, for
# greater consistency between gunicorn and
./manage.py runserver. See:
# [http://whitenoise.evans.io/en/stable/django.html#using-whitenoise-in-development](http://whitenoise.evans.io/en/stable/django.html#using-whitenoise-in-development)
'whitenoise.runserver_nostatic'``,
'django.contrib.staticfiles'``,
'main'``,
]
|
把main.urls
到根URL架构:
|
1
2
3
4
5
6
7
|
from
django.conf.urls ``import
url, include
from
django.contrib ``import
admin
urlpatterns ``=
[
url(r``'^'``, include(``'main.urls'``)),
url(r``'^admin/'``, admin.site.urls),
]
|
为了正确地显示表单,使用样式和类以及所有内容,我们需要安装django-widget-tweaks
:
|
1
2
|
$ pip ``install
django-widget-tweaks
$ pip freeze > requirements.txt
|
加django-widget-tweaks
到INSTALLED_APPS
:
|
1
2
3
4
5
|
INSTALLED_APPS ``=
[
# ...
'main'``,
'widget_tweaks'``,
]
|
我们现在把这两件事加到settings.py
:
-
LOGIN_REDIRECT_URL
::告诉Django在成功身份验证后在何处重定向用户。 -
LOGOUT_REDIRECT_URL
::告诉Django在用户注销后在哪里重定向。
|
1
2
3
4
|
# settings.py
LOGIN_REDIRECT_URL ``=
'dashboard'
LOGOUT_REDIRECT_URL ``=
'login'
|
让我们编写一个简单的主模板base.html
和一个dashboard.html
扩展它的模板。我们一会儿再去仪表盘。
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<!``DOCTYPE
html>
<``html``>
<``head
lang``=``"en"``>
<``meta
charset``=``"UTF-8"``>
<``link
rel``=``"stylesheet"``href``=``"[https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css](https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css)"``/>
<``title``>{% block title %}{% endblock %}</``title``>
</``head``>
<``body``>
<``div
class``=``"container"``>
{% block content %}{% endblock %}
`</div
>```
</``body``>
</``html``>
|
|
1
2
3
4
5
6
7
|
{% extends 'base.html' %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<``h1``>Dashboard</``h1``>
{% endblock %}
|
写入呈现dashboard.html
模板:
|
1
2
3
4
5
6
7
|
from
django.shortcuts ``import
render
from
django.core.urlresolvers ``import
reverse_lazy
@login_required``(login_url``=``reverse_lazy(``'login'``))
def
dashboard(request):
return
render(request, ``'dashboard.html'``)
|
我们都准备好了。到http://localhost:8000/login/
并测试身份验证是否有效。接下来,保存您的进度:
|
1
2
|
$ git add .
$ git commit -m``"Login/Logout/Dashboard views"
|
创建优惠券模型
现在,我们已经到了应用程序中最重要的部分,设计优惠券模型。我们会安装django-model-utils
在我们的模型中添加一些额外的属性。
|
1
2
|
$ pip ``install
django-model-utils
$ pip freeze > requirements.txt
|
编写Coupon
模型:
|
01
02
03
04
05
06
07
08
09
10
|
from
model_utils.models ``import
TimeStampedModel, TimeFramedModel
from
django.db ``import
models
from
django.contrib.auth.models ``import
User
class
Coupon(TimeStampedModel, TimeFramedModel):
owner ``=
models.ForeignKey(User)
discount_code ``=
models.CharField(``"Discount Code"``, max_length``=``100``)
website ``=
models.URLField(``"Website"``)
description ``=
models.TextField(``"Coupon Description"``)
|
这个django-model-utils
我们扩展的模型使我们能够:
-
TimeStampedModel
通过created
技术领域。 -
TimeFramedModel
添加start
和end
我们的模型。我们使用这些字段来跟踪优惠券的可用性。
将模型挂钩到管理员:
|
1
2
3
4
5
6
7
|
from
django.contrib ``import
admin
from
.models ``import
Coupon
@admin``.register(Coupon)
class
CouponAdmin(admin.ModelAdmin):
pass
|
创建和应用迁移:
|
1
2
|
$ .``/manage``.py makemigrations
$ .``/manage``.py migrate
|
保存进度:
|
1
2
|
$ git add .
$ git commit -m``"Create Coupon model"
|
用于创建优惠券的ModelForm
Django的一个很酷的特性是能够从模型类创建表单。我们将创建这样一个表单,使用户能够创建优惠券。让我们创建一个forms.py
文件中的main
申请:
|
1
2
3
4
5
6
7
8
|
from
django.forms ``import
ModelForm
from
.models ``import
Coupon
class
CouponForm(ModelForm):
class
Meta:
model ``=
Coupon
exclude ``=
(``'owner'``, ) ``# We're setting this field ourselves
|
让我们将此表单添加到仪表板中。我们需要同时更改视图和模板:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
# views.py
from
django.shortcuts ``import
render, redirect
from
django.core.urlresolvers ``import
reverse_lazy
from
.forms ``import
CouponForm
@login_required``(login_url``=``reverse_lazy(``'login'``))
def
dashboard(request):
if
request.method ``=``=
'POST'``:
form ``=
CouponForm(request.POST)
if
form.is_valid():
coupon ``=
form.save(commit``=``False``)
coupon.owner ``=
request.user
coupon.save()
return
redirect(``'dashboard'``)
else``:
form ``=
CouponForm()
return
render(request, ``'dashboard.html'``, context``=``{``'create_form'``: form})
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
{% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<``h1``>Dashboard</``h1``>
<``form
method``=``"post"``>
{% csrf_token %}
<``div
class``=``"form-group"``>
<``label
for``=``"discount_code"``>Discount Code</``label``>
{% render_field create_form.discount_code class="form-control" placeholder="Discount Code" %}
</``div``>
<``div
class``=``"form-group"``>
<``label
for``=``"website"``>Website</``label``>
{% render_field create_form.website class="form-control" placeholder="Website" %}
</``div``>
<``div
class``=``"form-group"``>
<``label
for``=``"description"``>Description</``label``>
{% render_field create_form.description class="form-control" placeholder="Description" %}
</``div``>
<``div
class``=``"form-group"``>
<``label
for``=``"start"``>Available From</``label``>
{% render_field create_form.start class="form-control" placeholder="Available From (MM/DD/YYYY)" %}
</``div``>
<``div
class``=``"form-group"``>
<``label
for``=``"end"``>Expires on</``label``>
{% render_field create_form.end class="form-control" placeholder="Expires On (MM/DD/YYYY)" %}
</``div``>
<``button
type``=``"submit"
class``=``"btn btn-primary"``>Save</``button``>
</``form``>
{% endblock %}
|
我们现在有了从仪表板创建优惠券的方法。去试试吧。我们无法在仪表板上看到优惠券,但我们可以在管理面板中这样做。让我们保存进展:
|
1
2
|
$ git add .
$ git commit -m``"Coupon creation form in dashboard"
|
即将到期的优惠券
我们还想在仪表板中显示另一件事:即将过期的优惠券,例如本周到期的优惠券。
加django.contrib.humanize
到INSTALLED_APPS
若要以友好的方式在模板中显示日期,请执行以下操作。
让我们增强视图,以便它获取即将到期的优惠券并将它们传递给模板上下文:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
from
datetime ``import
timedelta
from
django.contrib.auth.decorators ``import
login_required
from
django.shortcuts ``import
render, redirect
from
django.core.urlresolvers ``import
reverse_lazy
from
django.utils ``import
timezone
from
.forms ``import
CouponForm
from
.models ``import
Coupon
@login_required``(login_url``=``reverse_lazy(``'login'``))
def
dashboard(request):
expiring_coupons ``=
Coupon.objects.``function filter() { [native code] }``(
end__gte``=``timezone.now(),
end__lte``=``timezone.now() ``+
timedelta(days``=``7``))
if
request.method ``=``=
'POST'``:
form ``=
CouponForm(request.POST)
if
form.is_valid():
coupon ``=
form.save(commit``=``False``)
coupon.owner ``=
request.user
coupon.save()
return
redirect(``'dashboard'``)
else``:
form ``=
CouponForm()
return
render(request, ``'dashboard.html'``, context``=``{
'create_form'``: form,
'expiring_coupons'``: expiring_coupons})
|
让我们更新模板,以便它以表格的方式显示过期的优惠券。我们还将使用Bootstrap的网格系统将创建表单和表放在两个单独的列中:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
{% extends 'base.html' %}
{% load widget_tweaks %}
{% load humanize %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<``h1``>Dashboard</``h1``>
<``div
class``=``"row"``>
<``div
class``=``"col-md-6"``>
[The form code]
</``div``>
<``div
class``=``"col-md-6"``>
{% if expiring_coupons %}
<``table
class``=``"table"``>
<``thead``>
<``tr``>
<``th``>Discount Code</``th``>
<``th``>Website</``th``>
<``th``>Expire Date</``th``>
</``tr``>
</``thead``>
{% for coupon in expiring_coupons %}
<``tr``>
<``td``>{{coupon.discount_code}}</``td``>
<``td``>{{coupon.website}}</``td``>
<``td``>{{coupon.end|naturalday }}</``td``>
</``tr``>
{% endfor %}
</``table``>
{% else %}
<``div
class``=``"alert alert-success"
role``=``"alert"``>No coupons expiring soon</``div``>
{% endif %}
{% endblock %}
</``div``>
</``div``>
|
看上去不错。保存你的进展:
|
1
2
|
$ git add .
$ git commit -m``"Implementing the expiring coupon list"
|
目录视图
现在让我们学习一些其他Django快捷方式,以创建一个显示我们可用的优惠券列表的视图。我们讨论的是通用视图。下面是如何快速创建一个ListView
:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
# views.py
# ...
from
django.views.generic.``list
import
ListView
from
django.db.models ``import
Q
class
CouponListView(ListView):
model ``=
Coupon
def
get_queryset(``self``):
return
Coupon.objects.``function filter() { [native code] }``(Q(end__gte``=``timezone.now()) | Q(end__isnull``=``True``)).order_by(``'-end'``)
|
现在,将视图绑定到URL模式中:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
# main/urls.py
from
django.conf.urls ``import
url
from
django.contrib.auth ``import
views as auth_views
from
django.views.generic.base ``import
RedirectView
from
.views ``import
dashboard, CouponListView
urlpatterns ``=
[
url(``'^$'``, RedirectView.as_view(url``=``'login'``), name``=``'index'``),
url(r``'^login/$'``, auth_views.LoginView.as_view(), name``=``'login'``),
url(r``'^logout/$'``, auth_views.LogoutView.as_view(), name``=``'logout'``),
url(r``'^dashboard/$'``, dashboard, name``=``'dashboard'``),
url(r``'^catalogue/$'``, CouponListView.as_view(template_name``=``'catalogue.html'``), name``=``'catalogue'``),
]
|
创建模板catalogue.html
:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
{% extends 'base.html' %}
{% load humanize %}
{% block title %}Catalogue{% endblock %}
{% block content %}
<``h1``>Catalogue</``h1``>
<``div
class``=``"row"``>
<``div
class``=``"col-md-12"``>
{% if object_list %}
<``table
class``=``"table"``>
<``thead``>
<``tr``>
<``th``>Discount Code</``th``>
<``th``>Website</``th``>
<``th``>Expire Date</``th``>
</``tr``>
</``thead``>
{% for coupon in object_list %}
<``tr``>
<``td``>{{coupon.discount_code}}</``td``>
<``td``>{{coupon.website}}</``td``>
<``td``>{{coupon.end|naturalday }}</``td``>
</``tr``>
{% endfor %}
</``table``>
{% else %}
<``div
class``=``"alert alert-success"
role``=``"alert"``>
No coupons yet. Create your first one <``a``href``=``"{% url 'dashboard' %}"``>here</``a``>.
</``div``>
{% endif %}
{% endblock %}
</``div``>
</``div``>
|
既然我们把一切都连接起来了,就去http://localhost:8000/catalogue/
查看您的优惠券目录。
保存进度:
|
1
2
|
$ git add .
$ git commit -m``"Creating the catalogue view"
|
这离MVP很近。我鼓励你做一些微调,比如创建一个导航条,登录/注销/注册按钮等等。重要的是你了解原型化的过程,让人们看到你的产品。说到这里,我们的产品还没有上线。我们没有把最新版本推给Heroku。让我们这样做,然后拿起电话,打电话给投资者。
结语
我们创建了一个简单但实用的应用程序。我们快速地创建了特性,并且我们已经将它们部署到网上,这样我们的潜在客户就可以使用它们并给我们反馈。向人们展示比只谈论一个想法更好。
以下是我们可以得出的一些结论:
- 选择正确的工具可以大大加快开发过程。
- 用于原型开发的工具并不总是成熟项目的最佳选择。记住这一点,最好尽早使用更敏捷的工具并对它们进行迭代,而不是在一开始就迷失在细微的实现细节中。
- 利用PaaS意味着应用程序必须尊重一些设计模式。通常这些模式是有意义的,它们迫使我们编写更好的代码。
- Django有很多捷径可以让我们的生活更轻松:
- Django ORM有助于数据库访问。无需担心编写正确的SQL和对语法错误格外小心。
- 迁移帮助我们在数据库模式上进行版本和迭代。不需要编写SQL来创建表或添加列。
- Django有一个插件友好的架构。我们可以安装应用程序,帮助我们更快地实现我们的目标。
- 泛型视图和模型表单可以注入一些最常见的行为:列出模型、创建模型、身份验证、重定向等等。
- 在发射的时候,瘦和快是很重要的。不要浪费时间,也不要花钱。
网友评论