Giter Club home page Giter Club logo

albumy's Introduction

Albumy

Capture and share every wonderful moment.

Example application for Python Web Development with Flask (《Flask Web 开发实战》).

Demo: http://albumy.helloflask.com

Screenshot

Installation

clone:

$ git clone https://github.com/greyli/albumy.git
$ cd albumy

create & activate virtual env then install dependency:

with venv/virtualenv + pip:

$ python -m venv env  # use `virtualenv env` for Python2, use `python3 ...` for Python3 on Linux & macOS
$ source env/bin/activate  # use `env\Scripts\activate` on Windows
$ pip install -r requirements.txt

or with Pipenv:

$ pipenv install --dev
$ pipenv shell

generate fake data then run:

$ flask forge
$ flask run
* Running on http://127.0.0.1:5000/

Test account:

License

This project is licensed under the MIT License (see the LICENSE file for details).

albumy's People

Contributors

bbbbx avatar brandozhang avatar greyli avatar imyufanli avatar meizhaohui avatar yuxiaoy1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

albumy's Issues

Convert to webp

Can you make uploaded images convert to webp format? cause filesize is smaller than jpg so you save storage space

修复已知bug和部分个人向的改进

由于代码是在学习本章时从头开始写的,所以没有fork,部分代码排版也不相同,为了不影响albumy本来的排版,所以放在issue中,希望能帮助看到的人和作者。
https://github.com/amchii/FlaskAlbumy
README.md:

来自于李辉的Flask书上的示例程序:Albumy , 个人意见向地改进了少许代码,修复了一些已知bug :

  1. 新评论未进行photo.can_comment验证的bug

    if not photo.can_comment

  2. 收藏关注时登陆后的跳转问题

    if request.method.lower() == 'get':

  3. 鼠标在头像上悬浮的时候出现的用户信息不能即时更新

    $el.popover('dispose');

  4. 关注页分页的bug

    pagination = user.following.filter(Follow.followed_id != user.id).paginate(page=page, per_page=per_page)

  5. 评论区评论时间的tooltip显示当前时间的bug

    ​ 给tooltip的title传递函数可以正常显示,和Bluelog相同。

  6. 更换头像时若上传图片后不更新头像,则user.avatar_raw会被取代

    ​ 这个通过给User新建一个avatar_raw_temp字段,用于保存上传头像原图时的文件名。在 change_avatar.html 和更换头像的视图函数中通过has_temp参数进行判断

代码清单9-31 edit description script.js show hide, edge浏览器的锅

第347页, 代码清单9-31 albumy/templates/main/_photo_sidebar.html 描述与表单
发现问题: 点击 右边栏里面的 edit description , 无法显示出编辑照片描述的框
浏览器: Microsoft Edge
版本: 版本 91.0.864.48 (官方内部版本) (64 位)Microsoft Edge 是最新版本。
以为是js 的 hide show代码写错了, 于是就检查代码, 发现没问题, 又去检查了css, html代码, 当然这和css html代码没有关系, 还是检查了几遍, 后来直接重启了, 还是无法显示.
后来, 去pipenv 降级了bootstrap-flask 到1.04, 还是没有解决问题.
于是去找了电脑 里面的IE老浏览器, 我去, 能显示出来描述表单了,确定就是Edge浏览器的锅.
真是折腾人啊, 写出来希望后面的人不要再踩这个坑了.

第一个评论跳转到404

admin登陆后,上传图片,并自己评论自己时,第一条评论跳404,url显示/photo/31?page=0,在new_comment中的page=request.args.get('page',1,type=int)好像没有设置成功,然后自己评论第二次url才会显示/photo/31?page=1

仓库中代码与书中描述不匹配

书中第304页,按注意中git checkout skeleton签出程序后,代码结构与304-305页显示的程序包目录结构不一致。
图片
在第8章的个人博客部分也存在此情况。

Error: No such command "forge". please help.

(albumy-master) [miaohf@localhost albumy-master]$ flask forge
Usage: flask [OPTIONS] COMMAND [ARGS]...

Error: No such command "forge".
(albumy-master) [miaohf@localhost albumy-master]$ exit
exit
[miaohf@localhost albumy-master]$ su
Password:
[root@localhost albumy-master]# pip install faker
Requirement already satisfied: faker in /usr/lib64/python3.6/site-packages (1.0.5)
Requirement already satisfied: python-dateutil>=2.4 in /usr/lib/python3.6/site-packages (from faker) (2.8.0)
Requirement already satisfied: six>=1.10 in /usr/lib/python3.6/site-packages (from faker) (1.12.0)
Requirement already satisfied: text-unidecode==1.2 in /usr/lib/python3.6/site-packages (from faker) (1.2)
You are using pip version 19.0.2, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[root@localhost albumy-master]# exit
exit
[miaohf@localhost albumy-master]$ pipenv shell
Launching subshell in virtual environment…
[miaohf@localhost albumy-master]$ . /home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/bin/activate
(albumy-master) [miaohf@localhost albumy-master]$ flask forge
Usage: flask [OPTIONS] COMMAND [ARGS]...

Error: No such command "forge".
(albumy-master) [miaohf@localhost albumy-master]$ exit
exit
[miaohf@localhost albumy-master]$
[miaohf@localhost albumy-master]$
[miaohf@localhost albumy-master]$ pipenv install --dev
Installing dependencies from Pipfile.lock (15e1b5)…
Ignoring ipaddress: markers 'python_version == "2.7"' don't match your environment
Looking in indexes: https://pypi.python.org/simple
🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 30/30 — 00:00:14
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
[miaohf@localhost albumy-master]$ pipenv shell
Launching subshell in virtual environment…
[miaohf@localhost albumy-master]$ . /home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/bin/activate
(albumy-master) [miaohf@localhost albumy-master]$ flask forge
Usage: flask [OPTIONS] COMMAND [ARGS]...

Error: No such command "forge".
(albumy-master) [miaohf@localhost albumy-master]$ flask run -h 0.0.0.0

  • Serving Flask app "wsgi.py" (lazy loading)
  • Environment: development
  • Debug mode: on
  • Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
  • Restarting with inotify reloader
  • Debugger is active!
  • Debugger PIN: 170-913-317
    192.168.3.156 - - [03/Jun/2019 17:17:27] "GET / HTTP/1.1" 500 -
    Traceback (most recent call last):
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/cli.py", line 325, in call
    self._flush_bg_loading_exception()
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/cli.py", line 313, in _flush_bg_loading_exception
    reraise(*exc_info)
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/cli.py", line 302, in _load_app
    self._load_unlocked()
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/cli.py", line 317, in _load_unlocked
    self._app = rv = self.loader()
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/cli.py", line 372, in load_app
    app = locate_app(self, import_name, name)
    File "/home/miaohf/.local/share/virtualenvs/albumy-master-YKY7lYtz/lib/python3.6/site-packages/flask/cli.py", line 246, in locate_app
    'Could not import "{name}".'.format(name=module_name)
    flask.cli.NoAppException: Could not import "wsgi".
    192.168.3.156 - - [03/Jun/2019 17:17:27] "GET /?debugger=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
    192.168.3.156 - - [03/Jun/2019 17:17:27] "GET /?debugger=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
    192.168.3.156 - - [03/Jun/2019 17:17:27] "GET /?debugger=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
    192.168.3.156 - - [03/Jun/2019 17:17:27] "GET /?debugger=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
    192.168.3.156 - - [03/Jun/2019 17:17:27] "GET /?debugger=yes&cmd=resource&f=ubuntu.ttf HTTP/1.1" 200 -

联结查询

1、pagination = Photo.query
.join(Follow, Follow.followed_id == Photo.author_id)
.filter(Follow.follower_id == current_user.id)
.order_by(Photo.timestamp.desc())
.paginate(page, per_page)

2、pagination = Photo.query
.join(Follow, Follow.followed_id == Photo.author_id) \此时联结表中只包含被关注者的图片记录,
相当于a关注b,c,此时已得到b, c,的图片记录,

3、 .filter(Follow.follower_id == current_user.id) \在加一个过滤是不是有点多余???

改进意见

既然可以上传,所有应当可以下载,创建一个下载视图

有bug

当用户已经收藏了某一图片后,(前提)
当此用户登录时,此时图片边栏显示uncollect,
### 当此用户没有登录时,此时图片边栏显示collect, 但是此时点击Collect按钮,报错
Method Not Allowed
The method is not allowed for the requested URL.

注册用户时, follow 表插入错误

使用 register 注册新用户时, 容易出现错误:

sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: follow.follower_id, follow.followed_id
[SQL: INSERT INTO follow (follower_id, followed_id, timestamp) VALUES (?, ?, ?)]
[parameters: (12, 12, '2020-12-24 11:50:14.301453')]

解决方法是:
# self.follow(self) # follow self
或者提供静态方法:
检查自己是否已经 follow 自己.

    @staticmethod
    def add_self_follows():
        for user in User.query.all():
            if not user.is_following(user):
                user.follow(user)
        db.session.add(user)
        db.session.commit()

关于「9.4.3 写入角色与权限」小节有关代码设计的讨论

首先,「Albumy程序中定义的角色及对应权限」表格有六个角色(GuestBlokcedLockedUserModeratorAdministrator),然后转换为下面的字典,功能上是没有问题的。

roles_permissions_map = {
    'Locked': ['FOLLOW', 'COLLECT'],
    'User': ['FOLLOW', 'COLLECT', 'COMMENT' 'UPLOAD'],
    'Moderator': ['FOLLOW', 'COLLECT', 'COMMENT', 'UPLOAD', 'MODERATE'],
    'Administrator': ['FOLLOW', 'COLLECT', 'COMMENT', 'UPLOAD', 'MODERATE', 'ADMINISTER']
     }

但是,在设计风格上,我觉得上面的字典没有完全体现出设计者的设计思路,也就是定义好的所有角色与权限。

这个问题,有点类似于数据库中的字符串字段如果为空,应该设置为NULL好还是空字符串""好。

如果我来设计,会把GuestBlokced也加上去,但是,value为[]

roles_permissions_map = {
    'Guest': [],
    'Blokced': [],
    'Locked': ['FOLLOW', 'COLLECT'],
    'User': ['FOLLOW', 'COLLECT', 'COMMENT' 'UPLOAD'],
    'Moderator': ['FOLLOW', 'COLLECT', 'COMMENT', 'UPLOAD', 'MODERATE'],
    'Administrator': ['FOLLOW', 'COLLECT', 'COMMENT', 'UPLOAD', 'MODERATE', 'ADMINISTER']
     }

这样在代码层次上,就能体现出还有访客被封禁用户这两个角色,只不过这两个角色什么权限也没有。

当然,考虑到后面的代码会把字典的key加入数据库,如果不希望添加到数据库或是别的原因,可以对之进行注释(而不是从代码里删除):

roles_permissions_map = {
    # 'Guest': [],
    # 'Blokced': [],
    'Locked': ['FOLLOW', 'COLLECT'],
    'User': ['FOLLOW', 'COLLECT', 'COMMENT' 'UPLOAD'],
    'Moderator': ['FOLLOW', 'COLLECT', 'COMMENT', 'UPLOAD', 'MODERATE'],
    'Administrator': ['FOLLOW', 'COLLECT', 'COMMENT', 'UPLOAD', 'MODERATE', 'ADMINISTER']
     }

以上是我想表达的观点:「代码应尽量完全表达设计者的思路」。这样其他人一看到这个字典,喔~设计者定义了六个角色,有这些那些权限。而不是一看代码,设计者好像只定义了四个角色,有这些那些权限,然后去看设计文档,原来是六个角色(正如书中必须写点提示,解释为什么代码里没有另外两个角色)。

一点小小的探讨,如有不妥,还请见谅。

admin主页的Photo板块,reported显示错误

templates/amin/index.html中:

<p class="card-text">Reported: {{ reported_photo_count |default('0') }}</p>
改为
<p class="card-text">Reported: {{ reported_photos_count |default('0') }}</p>

重置密码的URL的问题

utils中的generate_token函数返回的s.dump(data)是bytes类型, 使用url_for组织URL最终会将单引号转义为%27token就变成了b%27....%27,无法验证, 建议改一个decode()

In the utils.py file, generate_token() return s.dumps() is bytes class, when I use url_for() to generate resetpassword url, the token well like b%27......%27, and can't validate. maybe you can change to s.dumps(data).decode().

Python : 3.6
Flask: 1.02

关注页分页bug

{% if follows|length != 1 %}

程序设置每页展示20用户,当关注数为21时第2页的follows|length为1,却被这个if给过滤掉了。
还有用户初始化时会首先关注自己,当获取第一页关注用户时
pagination = user.followers.paginate(page, per_page)
会同时获取到自己,然后被
{% if follow.follower != user %}
这条语句过滤掉,这就导致第一页会只显示19个用户

放到云服务器上运行报错!

Traceback (most recent call last):
File "/root/Envs/albumy/lib/python3.7/site-packages/flask/cli.py", line 330, in call
rv = self._load_unlocked()
File "/root/Envs/albumy/lib/python3.7/site-packages/flask/cli.py", line 317, in _load_unlocked
self._app = rv = self.loader()
File "/root/Envs/albumy/lib/python3.7/site-packages/flask/cli.py", line 372, in load_app
app = locate_app(self, import_name, name)
File "/root/Envs/albumy/lib/python3.7/site-packages/flask/cli.py", line 246, in locate_app
'Could not import "{name}".'.format(name=module_name)
flask.cli.NoAppException: Could not import "albumy".

[email protected]无法登录

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: user.avatar_s [SQL: 'SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.password_hash AS user_password_hash, user.name AS user_name, user.website AS user_website, user.bio AS user_bio, user.location AS user_location, user.member_since AS user_member_since, user.avatar_s AS user_avatar_s, user.avatar_m AS user_avatar_m, user.avatar_l AS user_avatar_l, user.avatar_raw AS user_avatar_raw, user.confirmed AS user_confirmed, user.locked AS user_locked, user.active AS user_active, user.public_collections AS user_public_collections, user.receive_comment_notification AS user_receive_comment_notification, user.receive_follow_notification AS user_receive_follow_notification, user.receive_collect_notification AS user_receive_collect_notification, user.role_id AS user_role_id \nFROM user \nWHERE user.email = ?\n LIMIT ? OFFSET ?'] [parameters: ('[email protected]', 1, 0)] (Background on this error at: http://sqlalche.me/e/e3q8)

在消息提醒notification.py文件中的问题

我自己按照书中的提示在自行编写了新用户注册时发送消息提醒函数如下:
def push_confirm_notification(register):
reciver = User.query.filter_by(pl_num=current_app.config['ADMIN_PL_NUM']).first()
message = '用户<a href="%s">%s</a>已注册' % (url_for("main.show_user", user_id=register.id), register.name)
notification = Notification(message=message, reciver_id=reciver.id)
db.session.add(notification)
db.session.commit()
db.session.close()

但是每次注册新用户时总会出现错误,提示我werkzeug.routing.BuildError: Could not build url for endpoint 'main.show_user'. Did you forget to specify values ['user_id']?
请问这是什么原因导致的呢?希望大神给解答。

pipenv install --dev 的时候报错

pipenv install --dev 的时候报错
Installing dependencies from Pipfile.lock (15e1b5)...
An error occurred while installing ....
An error occurred while installing ....
An error occurred while installing ....
An error occurred while installing ....
An error occurred while installing ....
An error occurred while installing ....
An error occurred while installing ....

有知道怎么解决的吗

评论回复问题

1、当没有评论的时候,新建一条评论提交会跳转404(找不到页面),url中显示page=0
应该是 _comment.html 中的:


{{ render_form(comment_form, action=url_for('.new_comment', photo_id=photo.id,
page=pagination.pages),extra_classes="text-right") }}

这一片段,应为 page=pagination.pages or 1
2、评论回复功能实现不了,只能生成新评论;
3、当是自己的评论时,个人觉得不应该有举报按钮,不能自己举报自己
谢谢

更换邮箱源码有问题,或者作者忘记去改源码了?

我是初学者小白, 看这书有一段时间了, 消化起来有些慢, 不过好在每天都要跟着书和源码来敲代码学习.今天发现了一个问题就是更换邮箱能收到邮件, 却无法改掉数据库里的邮箱地址, 开始以为代码有问题, 再次检查源码发现了一个问题,

@user_bp.route('/settings/change-email', methods=['GET', 'POST'])
@fresh_login_required
def change_email_request():
form = ChangeEmailForm()
if form.validate_on_submit():
token = generate_token(user=current_user, operation=Operations.CHANGE_EMAIL, new_email=form.email.data.lower())
send_confirm_email(to=form.email.data, user=current_user, token=token) <--------->问题所在
flash('Confirm email sent, check your inbox.', 'info')
return redirect(url_for('.index', username=current_user.username))
return render_template('user/settings/change_email.html', form=form)

这个还是用的注册时的发确认邮件函数, 这个send_confirm_email会导致发去邮箱的url会是 http:xxxxxxxxx/auth/confirm/tokenxxxxxxxxxxx. 点击邮箱这段地址后,是无法触发 解密token的视图函数def change_email_request():的, 我看到之前有发过isure提问题的, 作者回复源码已经改好, 可是我发现还是无法改掉数据库里的邮箱地址, 也或者作者用了其它的方法, 不过下面是我的解决方法.

我按着作者前面的email.py里的confirm函数写一个新函数来发送change email 的邮件 , 照着confirm函数抄一遍, 不过template要改一下的, template=''emails/change_email_confirm/'', 再去email文件夹下写一个 change_email_confirm.html 和 change_email_confirm.txt,这两文件照抄confirm.html 和confirm.txt, 文件中的url要改一下, url_for('user.change-email', token=token, _external=True) , 这里的user.change-email就是user.py里面的解析token的def change_email函数.

关于权限过滤、搜索和个人主页的改进

一、个人主页
1、个人主页收藏处没有显示页码
二、搜索问题
1、输入空格搜索会出现BUG
三、管理员与协管员问题
1、在后台处,当登陆用户为管理员或者协管员时,后台用户管理处自己不应该能锁定或封禁自己,不然可能会不小心把自己给锁定或者封禁了
2、协管员权限过大,应该过滤一下,不然连超级管理员都能锁定、封禁
谢谢!

Photo下无法关闭评论

自己图片下disabled comment会

Method Not Allowed

The method is not allowed for the requested URL.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.