feat(backend): add video_url and video_position to properties

- Model: video_url VARCHAR(512) nullable, video_position VARCHAR(20) default 'section'
- Migration: k3l4m5n6o7p8_add_video_to_properties
- Admin route: expose/accept video_url (sanitize empty->NULL) and video_position
- Public schema: PropertyDetailOut exposes both fields
This commit is contained in:
MatheusAlves96 2026-04-22 23:57:28 -03:00
parent 7a53865408
commit d363a09f36
4 changed files with 40 additions and 1 deletions

View file

@ -51,6 +51,8 @@ class Property(db.Model):
index=True, index=True,
) )
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
video_url = db.Column(db.String(512), nullable=True)
video_position = db.Column(db.String(20), nullable=False, server_default='section')
photos = db.relationship( photos = db.relationship(
"PropertyPhoto", "PropertyPhoto",

View file

@ -4,7 +4,7 @@ import re
import bcrypt import bcrypt
from datetime import datetime, date from datetime import datetime, date
from decimal import Decimal from decimal import Decimal
from typing import Optional from typing import Optional, Literal
from flask import Blueprint, request, jsonify, current_app, send_from_directory from flask import Blueprint, request, jsonify, current_app, send_from_directory
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
@ -168,6 +168,8 @@ class PropertyAdminOut(BaseModel):
is_featured: bool is_featured: bool
photos: list[PhotoAdminOut] = [] photos: list[PhotoAdminOut] = []
amenity_ids: list[int] = [] amenity_ids: list[int] = []
video_url: Optional[str] = None
video_position: Literal['carousel', 'section'] = 'section'
@classmethod @classmethod
def from_prop(cls, p: Property) -> "PropertyAdminOut": def from_prop(cls, p: Property) -> "PropertyAdminOut":
@ -203,6 +205,8 @@ class PropertyAdminOut(BaseModel):
for ph in p.photos for ph in p.photos
], ],
amenity_ids=[a.id for a in p.amenities], amenity_ids=[a.id for a in p.amenities],
video_url=p.video_url,
video_position=p.video_position,
) )
@ -348,10 +352,15 @@ def admin_update_property(property_id: str):
"area_m2", "area_m2",
"city_id", "city_id",
"neighborhood_id", "neighborhood_id",
"video_position",
) )
for field in _SCALAR_FIELDS: for field in _SCALAR_FIELDS:
if field in body: if field in body:
setattr(prop, field, body[field]) setattr(prop, field, body[field])
# video_url: sanitizar e tratar string vazia como NULL
if 'video_url' in body:
raw = body['video_url']
prop.video_url = raw.strip() if raw and raw.strip() else None
# code: tratar string vazia como NULL # code: tratar string vazia como NULL
if "code" in body: if "code" in body:
prop.code = body["code"] if body["code"] else None prop.code = body["code"] if body["code"] else None

View file

@ -61,3 +61,5 @@ class PropertyDetailOut(PropertyOut):
address: str | None = None address: str | None = None
code: str | None = None code: str | None = None
description: str | None = None description: str | None = None
video_url: str | None = None
video_position: Literal['carousel', 'section'] = 'section'

View file

@ -0,0 +1,26 @@
"""add video to properties
Revision ID: k3l4m5n6o7p8
Revises: j2k3l4m5n6o7
Create Date: 2026-04-22
"""
import sqlalchemy as sa
from alembic import op
revision = 'k3l4m5n6o7p8'
down_revision = 'j2k3l4m5n6o7'
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column('properties',
sa.Column('video_url', sa.String(512), nullable=True))
op.add_column('properties',
sa.Column('video_position', sa.String(20),
nullable=False, server_default='section'))
def downgrade() -> None:
op.drop_column('properties', 'video_position')
op.drop_column('properties', 'video_url')