reflex icon indicating copy to clipboard operation
reflex copied to clipboard

Image upload

Open AnonimPython opened this issue 8 months ago • 6 comments

Hi Developers!

I have a problem with upload for pictures reflex does not have dynamic page reload. When a file with a photo is placed and saved in the database, it is dynamically added to the bottom of the page (for testing) and I noticed that when you add pictures, they are not displayed on the screen. But when you reload the server - everything is there. Agree, this is not convenient to add a gallery - you need to reload the page, which is not very convenient, especially if the project is already in operation. Tell me where my mistakes are?

Code:

from datetime import datetime
from sqlmodel import select, Field
from typing import Optional
import uuid


class Image(rx.Model, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    filename: str = Field(max_length=255)
    caption: str = Field(max_length=500)
    path: str = Field(max_length=255)
    created_at: datetime = Field(default_factory=datetime.utcnow)

class State(rx.State):
    """The app state."""
    images: list[Image] = []
    caption: str = ""
    processing: bool = False
    progress: int = 0
    
    def on_load(self):
        """Load existing images on page load."""
        with rx.session() as session:
            self.images = session.exec(
                select(Image).order_by(Image.created_at.desc())
            ).all()

    @rx.event
    async def handle_upload_progress(self, progress: dict):
        """Handle upload progress updates."""
        self.processing = True
        self.progress = round(progress["progress"] * 100)
        if self.progress >= 100:
            self.processing = False
            self.on_load()

    @rx.event
    async def handle_upload(self, files: list[rx.UploadFile]):
        if not files or not self.caption:
            return rx.window_alert("Please, add a caption for image")
        
        for file in files:
            file_extension = file.name.split('.')[-1]
            unique_filename = f"{uuid.uuid4()}.{file_extension}"
            
            
            upload_dir = rx.get_upload_dir()
            outfile = upload_dir / unique_filename
            
            
            upload_data = await file.read()
            with outfile.open("wb") as file_object:
                file_object.write(upload_data)

            try:
                file_url = f"/_upload/{unique_filename}"
                
                with rx.session() as session:
                    db_image = Image(
                        filename=unique_filename,
                        caption=self.caption,
                        path=file_url 
                    )
                    session.add(db_image)
                    session.commit()
                    session.refresh(db_image)
                    
                    
                    self.images = [db_image] + self.images
                        
            except Exception as e:
                print(f"Database error: {e}")
                return rx.window_alert("Error creating database entry.")
        
        # Очищаем поле caption
        self.caption = ""
        # Перезагружаем список изображений
        self.on_load()

def image_card(image: rx.Var[Image]):
    return rx.vstack(
        rx.image(
            src=image.path,
            height="200px",
            width="auto",
        ),
        rx.text(image.caption),
        rx.moment(
            image.created_at,
            format="YYYY-MM-DD HH:mm:ss"
        ),
        padding="5",
        border="1px solid #eaeaea",
        border_radius="md",
        width="100%",
    )

def index():
    return rx.vstack(
        rx.vstack(
            rx.heading("Add new image"),
            rx.text_area(
                placeholder="Caption for image",
                value=State.caption,
                on_change=State.set_caption,
                margin_bottom="5",
            ),
            rx.upload(
                rx.vstack(
                    rx.button(
                        "Выберите файл",
                        color="rgb(107,99,246)",
                        bg="white",
                        border="1px solid rgb(107,99,246)",
                    ),
                    rx.text(
                        "upload image..."
                    ),
                ),
                id="upload1",
                border="1px dotted rgb(107,99,246)",
                padding="2em",
                accept={
                    "image/png": [".png"],
                    "image/jpeg": [".jpg", ".jpeg"],
                },
                margin_bottom="5",
            ),
            rx.hstack(
                rx.foreach(
                    rx.selected_files("upload1"),
                    rx.text
                )
            ),
            rx.hstack(
                rx.button(
                    "Upload",
                    on_click=State.handle_upload(
                        rx.upload_files(
                            upload_id="upload1",
                            on_upload_progress=State.handle_upload_progress,
                        )
                    ),
                ),
                rx.button(
                    "Clear",
                    on_click=rx.clear_selected_files("upload1"),
                ),
                spacing="5",
            ),
            rx.cond(
                State.processing,
                rx.vstack(
                    rx.text(f"Загрузка: {State.progress}%"),
                    rx.progress(value=State.progress, max=100),
                ),
            ),
            padding="2em",
            border="1px solid #eaeaea",
            border_radius="10px",
            width="100%",
            margin_bottom="2em",
        ),
        
        rx.heading("Gallery"),
        rx.vstack(
            rx.foreach(
                State.images,
                image_card,
            ),
            spacing="5", 
            width="100%",
        ),
        width="100%",
        max_width="800px",
        margin="0 auto",
        padding="2em",
        on_mount=State.on_load,
    )
    
    

app = rx.App()
app.add_page(index, route="/")```

To start project:
reflex db init 
reflex db makemigrations
reflex db migrate
reflex run

AnonimPython avatar Apr 01 '25 21:04 AnonimPython

Hi! I'd like to work on this issue. Could you please assign it to me?

vjabhi000985 avatar Apr 11 '25 08:04 vjabhi000985

Hi! I'd like to work on this issue. Could you please assign it to me?

Hi! Yes, take this.

AnonimPython avatar Apr 11 '25 17:04 AnonimPython

@AnonimPython Thank you so much for this opportunity. Can you provide a reference to this code block? It would be helpful.

vjabhi000985 avatar Apr 12 '25 13:04 vjabhi000985

@AnonimPython Thank you so much for this opportunity. Can you provide a reference to this code block? It would be helpful.

Yes! Ok, I will create a repo for u and give a link.

AnonimPython avatar Apr 16 '25 11:04 AnonimPython

@AnonimPython I will be waiting for your response.

vjabhi000985 avatar Apr 16 '25 17:04 vjabhi000985