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
Branches
Tags release/user_oidc/6.0.0
No related merge requests found
......@@ -178,7 +178,37 @@ def item_delete(item_id):
@app.route('/item/<item_id>/photo', methods=['POST'])
def item_upload_photo(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()
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):
qr_code = db.Column(db.String(128), nullable=False, unique=True, default=token_qrfriendly)
name = db.Column(db.String(128), nullable=False)
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')
location_id = db.Column(db.String(5), db.ForeignKey('location.id'))
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 @@
{% for item in page.items %}
<tr>
<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">
<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>
{% endif %}
</td>
......
......@@ -37,35 +37,14 @@
<div class="row">
<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.photos|length > 1 %}
<div class="carousel-indicators">
{% 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%;">
{% if item.photo %}
<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">
<img src="{{ url_for('photo_medium', photo_id=item.photo.id) }}" style="max-width: 100%; max-height: 100%;">
</a>
</div>
{% endfor %}
</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>
{% else %}
<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>
{% endif %}
</div>
</div>
<div class="col-12 col-md-8">
<h1>{{ item.name }}</h1>
<p class="h5 text-muted">{{ item.id }}</p>
......@@ -74,4 +53,37 @@
</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 %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment