Skip to content
Snippets Groups Projects
Commit 68f5f767 authored by Julian's avatar Julian
Browse files

Add photo deletion and primary photo selection

parent 8594aae4
No related branches found
No related tags found
No related merge requests found
...@@ -178,7 +178,37 @@ def item_delete(item_id): ...@@ -178,7 +178,37 @@ def item_delete(item_id):
@app.route('/item/<item_id>/photo', methods=['POST']) @app.route('/item/<item_id>/photo', methods=['POST'])
def item_upload_photo(item_id): def item_upload_photo(item_id):
item = Item.query.get_or_404(item_id) item = Item.query.get_or_404(item_id)
item.photos.append(Photo.from_form(request.files['file'])) photo = Photo.from_form(request.files['file'])
item.photos.append(photo)
if not item.photo:
item.photo = photo
db.session.commit()
return redirect(url_for('item_view', item_id=item_id))
@app.route('/item/<item_id>/photo/<int:photo_id>/set', methods=['POST'])
def item_set_photo(item_id, photo_id):
item = Item.query.get_or_404(item_id)
photo = Photo.query.get_or_404(photo_id)
item.photo = photo
db.session.commit()
return redirect(url_for('item_view', item_id=item_id))
@app.route('/item/<item_id>/photo/clear', methods=['POST'])
def item_clear_photo(item_id):
item = Item.query.get_or_404(item_id)
item.photo = None
db.session.commit()
return redirect(url_for('item_view', item_id=item_id))
@app.route('/item/<item_id>/photo/<int:photo_id>/delete', methods=['GET', 'POST'])
def item_delete_photo(item_id, photo_id):
item = Item.query.get_or_404(item_id)
photo = Photo.query.get_or_404(photo_id)
if request.method == 'GET':
return render_template('item/delete_photo.html', item=item, photo=photo)
if item.photo == photo:
item.photo = None
item.photos.remove(photo)
db.session.commit() db.session.commit()
return redirect(url_for('item_view', item_id=item_id)) return redirect(url_for('item_view', item_id=item_id))
......
"""Primary item photo
Revision ID: bf26c4ca926e
Revises: aabfcc081e98
Create Date: 2023-04-23 19:33:11.072575
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'bf26c4ca926e'
down_revision = 'aabfcc081e98'
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table('item', schema=None) as batch_op:
batch_op.add_column(sa.Column('photo_id', sa.Integer(), nullable=True))
batch_op.create_foreign_key('fk_item_photo_id_photo', 'photo', ['photo_id'], ['id'])
item_photo = sa.table(
'item_photo',
sa.column('item_id', sa.String(5)),
sa.column('photo_id', sa.Integer())
)
item = sa.table(
'item',
sa.column('id', sa.String(5)),
sa.column('photo_id', sa.Integer())
)
conn = op.get_bind()
conn.execute(
sa.update(item).values(
photo_id=sa.select([item_photo.c.photo_id])
.where(item_photo.c.item_id == item.c.id)
.order_by(item_photo.c.photo_id)
.limit(1)
.scalar_subquery()
)
)
def downgrade():
with op.batch_alter_table('item', schema=None) as batch_op:
batch_op.drop_constraint('fk_item_photo_id_photo', type_='foreignkey')
batch_op.drop_column('photo_id')
...@@ -82,6 +82,8 @@ class Item(db.Model): ...@@ -82,6 +82,8 @@ class Item(db.Model):
qr_code = db.Column(db.String(128), nullable=False, unique=True, default=token_qrfriendly) qr_code = db.Column(db.String(128), nullable=False, unique=True, default=token_qrfriendly)
name = db.Column(db.String(128), nullable=False) name = db.Column(db.String(128), nullable=False)
description = db.Column(db.Text(), nullable=False, server_default='') description = db.Column(db.Text(), nullable=False, server_default='')
photo_id = db.Column(db.Integer(), db.ForeignKey('photo.id'))
photo = db.relationship('Photo')
photos = db.relationship('Photo', secondary='item_photo') photos = db.relationship('Photo', secondary='item_photo')
location_id = db.Column(db.String(5), db.ForeignKey('location.id')) location_id = db.Column(db.String(5), db.ForeignKey('location.id'))
location = db.relationship('Location') location = db.relationship('Location')
{% extends 'layout.html' %}
{% block body %}
<form method="POST" class="form">
<input type="hidden" name="csrf_token" value="{{ request.csrf_token }}">
<p>Really delete this photo from item {{ item.name }} ({{ item.id }}):</p>
<div style="width: 390px; height: 300px;" class="d-flex align-items-center justify-content-center border text-bg-light">
<img src="{{ url_for('photo_medium', photo_id=photo.id) }}" style="max-width: 100%; max-height: 100%;">
</div>
<div class="d-flex justify-content-end gap-1">
<a href="{{ url_for('item_view', item_id=item.id) }}" class="btn btn-light">Cancel</a>
<button type="submit" class="btn btn-danger">Delete photo</button>
</div>
</form>
{% endblock %}
...@@ -22,9 +22,9 @@ ...@@ -22,9 +22,9 @@
{% for item in page.items %} {% for item in page.items %}
<tr> <tr>
<td style="width: 140px;"> <td style="width: 140px;">
{% if item.photos %} {% if item.photo %}
<a href="{{ url_for('item_view', item_id=item.id) }}" style="width: 130px; height: 100px;" class="d-flex align-items-center justify-content-center border text-bg-light"> <a href="{{ url_for('item_view', item_id=item.id) }}" style="width: 130px; height: 100px;" class="d-flex align-items-center justify-content-center border text-bg-light">
<img src="{{ url_for('photo_small', photo_id=(item.photos|first).id) }}" style="max-width: 100%; max-height: 100%;"> <img src="{{ url_for('photo_small', photo_id=item.photo.id) }}" style="max-width: 100%; max-height: 100%;">
</a> </a>
{% endif %} {% endif %}
</td> </td>
......
...@@ -37,35 +37,14 @@ ...@@ -37,35 +37,14 @@
<div class="row"> <div class="row">
<div class="col-12 col-md-4"> <div class="col-12 col-md-4">
<div id="photo-carousel" class="carousel carousel-dark slide border text-bg-light p-1 my-2" style="width: 100%; height: 300px;"> {% if item.photo %}
{% if item.photos|length > 1 %} <a href="{{ url_for('photo_show', photo_id=item.photo.id) }}" style="width: 100%; height: 100%;" class="d-flex align-items-center justify-content-center border text-bg-light p-1">
<div class="carousel-indicators"> <img src="{{ url_for('photo_medium', photo_id=item.photo.id) }}" style="max-width: 100%; max-height: 100%;">
{% for photo in item.photos %}
<button type="button" data-bs-target="#photo-carousel" data-bs-slide-to="{{ loop.index0 }}" {% if loop.first %} class="active" aria-current="true" {% endif %}aria-label="Photo {{ loop.index }}"></button>
{% endfor %}
</div>
{% endif %}
<div class="carousel-inner" style="width: 100%; height: 100%;">
{% for photo in item.photos %}
<div class="carousel-item {{ 'active' if loop.first }}" style="width: 100%; height: 100%;">
<a href="{{ url_for('photo_show', photo_id=photo.id) }}" style="width: 100%; height: 100%;" class="d-flex align-items-center justify-content-center">
<img src="{{ url_for('photo_medium', photo_id=photo.id) }}" style="max-width: 100%; max-height: 100%;">
</a> </a>
</div> {% else %}
{% endfor %} <div style="width: 100%; height: 100%; min-width: 390px; min-height: 300px;" class="d-flex align-items-center justify-content-center border text-bg-light"></div>
</div>
{% if item.photos|length > 1 %}
<button class="carousel-control-prev" type="button" data-bs-target="#photo-carousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#photo-carousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
{% endif %} {% endif %}
</div> </div>
</div>
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<h1>{{ item.name }}</h1> <h1>{{ item.name }}</h1>
<p class="h5 text-muted">{{ item.id }}</p> <p class="h5 text-muted">{{ item.id }}</p>
...@@ -74,4 +53,37 @@ ...@@ -74,4 +53,37 @@
</div> </div>
</div> </div>
<h2>Photos</h2>
<table class="table table-hover">
<tbody>
{% for photo in item.photos %}
<tr>
<td style="width: 140px;">
<a href="{{ url_for('photo_show', photo_id=photo.id) }}" style="width: 100%; height: 100%;" class="d-flex align-items-center justify-content-center border text-bg-light">
<img src="{{ url_for('photo_small', photo_id=photo.id) }}" style="max-width: 100%; max-height: 100%;">
</a>
</td>
<td>
<p>Uploaded {{ photo.created.date() }}</p>
</td>
<td>
<div class="d-flex justify-content-end gap-1">
<a href="{{ url_for('item_delete_photo', item_id=item.id, photo_id=photo.id) }}" class="btn btn-danger">Delete</a>
{% if item.photo != photo %}
<form method="POST" action="{{ url_for('item_set_photo', item_id=item.id, photo_id=photo.id) }}">
<input type="hidden" name="csrf_token" value="{{ request.csrf_token }}">
<button type="submit" class="btn btn-primary">Make primary</button>
</form>
{% else %}
<form method="POST" action="{{ url_for('item_clear_photo', item_id=item.id) }}">
<input type="hidden" name="csrf_token" value="{{ request.csrf_token }}">
<button type="submit" class="btn btn-light">Clear primary</button>
</form>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %} {% endblock %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment