Maydonlar va vidjetlar

FastFort taniydigan har bir ustun turi, u chizadigan boshqaruv elementi va oddiy boshqaruv elementini chizish ma’lumotlaringizni buzib qo‘yadigan to‘rtta holat.

Mavjud v0.1.0

Boshqaruv elementini siz tanlamaysiz — buni ustun turi hal qiladi. O‘ttiz yetti xil maydon turi yigirma bitta boshqaruv elementiga mos keladi, va bu moslik — bitta jadval, har bir shablondagi shart-sharoit emas.

Moslik jadvali

TurBoshqaruv elementi
STRINGtext
TEXTtextarea
INTEGER · BIGINT · FLOATnumberChegaralar ustundan olinadi
DECIMALdecimalAniqlik va masshtab ustundan olinadi
MONEYmoney
BOOLEANcheckbox…NULL qabul qiladigan holatlar bundan mustasno — pastga qarang
DATEdateKalendar, uchta ko‘rinish
DATETIMEdatetimeKalendar va chizilgan soat
TIMEtime
DURATIONdurationTo‘rtta belgilangan raqam maydoni
UUIDtext
JSONjsonNoto‘g‘ri JSON’ni yubormaydi
HSTOREkeyvalueHar bir qatorda bitta key: value juftligi
ENUMselectA’zolar ustundan o‘qib olinadi
EMAIL · URLemail · url
PASSWORDpasswordYangi parol va uni tasdiqlash
FILE · IMAGEfile · imageYuklash kartochkasi, drag-and-drop
ARRAYtagsHar bir element element turi bo‘yicha tekshiriladi
RANGE · MULTIRANGErangeIkkita maydon va chegaralar tanlagichi
INETinetHost yoki tarmoqni qabul qiladi
MACADDRmac
BITSbitsFaqat 0 va 1
GEOMETRYgeometryMatn maydoni va xarita
VECTORtextareaAmalda faqat o‘qish uchun
BINARY · SEARCH_VECTORreadonlyKo‘rsatiladi, lekin hech qachon yozilmaydi
FOREIGN_KEY · ONE_TO_ONErelationQidiruvli tanlagich
MANY_TO_MANYrelationsO‘chiriladigan chiplar
UNKNOWNreadonlyFaqat o‘qish uchun qator, buzilgan sahifa emas

Ahamiyatli to‘rtta holat

Bu — oddiy boshqaruv elementini chizish ma’lumot yo‘qotilishiga olib keladigan holatlar.

NULL qabul qiluvchi mantiqiy (boolean) maydon — checkbox emas

Checkbox’da ikkita holat bor, ustunda esa — uchta. Uni checkbox sifatida chizsak, kimdir formani ochib «Saqlash»ni bosgan zahoti NULL Falsega aylanadi — bu hech kim so‘ramagan, sezilmagan ma’lumot o‘zgarishi.

two_factor_enabled: Mapped[bool | None] = mapped_column(default=None)

Uch holatli boshqaruv elementini oladi. «Belgilanmagan» — belgilanmagan bo‘lib qoladi.

Interval — matn maydoni emas

planned_duration: Mapped[dt.timedelta | None] = mapped_column(sa.Interval())

To‘rtta belgilangan maydon — kun, soat, daqiqa, soniya. 2 08:30:00 kabi formatni kutuvchi matn maydoni — bu kimgadir tushuntirib berish kerak bo‘lgan va baribir xato kiritiladigan format.

Uning ostidagi format bo‘yicha maslahat faqat skript ishlaganda ko‘rsatiladi, chunki skript maydonni to‘rtta belgilangan kiritish maydoniga almashtirgach, HH:MM:SS kabi yozish haqidagi ko‘rsatma allaqachon sahifada mavjud bo‘lmagan boshqaruv elementiga tegishli bo‘lib qoladi.

Hstore — Python dict’ining repr’i emas

attributes: Mapped[dict[str, str] | None] = mapped_column(postgresql.HSTORE())

Har bir qatorda bitta key: value jufti. Ro‘yxat katagida bu juftliklar shaklida ko‘rsatiladi, {'colour': 'black'} kabi emas — bu esa Python dasturchisi bo‘lmagan har qanday kishiga buzilgan ma’lumotdek ko‘rinadi.

Range’ga qavslar shart emas

dock_numbers: Mapped[Any | None] = mapped_column(postgresql.INT4RANGE())
contract_period: Mapped[Any | None] = mapped_column(postgresql.DATERANGE())

Ikkita maydon va chegaralar tanlagichi. Chekka nuqtalar uchun xuddi shu turdagi alohida ustun uchun ishlatiladigan boshqaruv elementlari jadvali qo‘llaniladi — shuning uchun daterangening ikkala maydoni oddiy Date ustuni oladigan xuddi shu kalendarni oladi, va ular bir-biridan chetlashib keta olmaydi.

MULTIRANGE esa xuddi shu boshqaruv elementi ichida har bir qatorda bitta range’ga siqiladi.

Bitta ustunni qayta belgilash

Ba’zi narsalarni ustunning o‘zi sizga aytib bera olmaydi. Yetti belgili satr faqat loyihangiz shunday deganligi uchun rang hisoblanadi.

class TagAdmin(admin.ModelAdmin):
    formfield_overrides = {"colour": "color"}
class ProductAdmin(admin.ModelAdmin):
    formfield_overrides = {
        "image": "image",         # a String holding a path
        "datasheet": "file",
        "description": "richtext",
    }

Vidjet nomlari e’lon qilish bosqichida tekshiriladi. Xato yozilgan nom — mavjud barcha nomlar ro‘yxati bilan chiqadigan xatolik, jim-jit faqat o‘qish uchun ko‘rsatiladigan maydon emas.

Turni hamma joyda qayta belgilash

Agar loyihangizda yigirmata modelda ishlatiladigan EmailAddress turi bo‘lsa, sizga email uchun boshqaruv elementi u ishlatilgan hamma joyda kerak — kimdir esiga tushirgan ba’zi modellardagina emas. Bir marta unutilgan formfield_overrides — bu hech kim ko‘rib chiqmagan sahifadagi oddiy matn maydonidagi parol xeshi degani.

import sqlalchemy as sa

from fastfort.orm.sqlalchemy import Classification, register_type
from fastfort.spec import FieldType


class EmailAddress(sa.types.TypeDecorator[str]):
    impl = sa.String
    cache_ok = True


class PasswordHash(sa.types.TypeDecorator[str]):
    impl = sa.String
    cache_ok = True


_RULES = {EmailAddress: FieldType.EMAIL, PasswordHash: FieldType.PASSWORD}


def _classify(column):
    for declared, field_type in _RULES.items():
        if isinstance(column.type, declared):
            return Classification(field_type)
    # Decline, and the next rule gets a turn. Every rule follows this contract.
    return None


def register_project_types() -> None:
    register_type(_classify, first=True)

Ikkita narsani to‘g‘ri qilish kerak:

first=True. Busiz bu qoidalar hech qachon ishlamaydi — String ustidagi TypeDecorator oxiriga qo‘shilgan qoidaga yetib borishdan ancha oldin o‘rnatilgan String qoidasiga mos kelib qoladi, va ustun oddiy matn sifatida tasniflanadi.

Buni biror narsa introspeksiya qilinishidan oldin chaqiring. Spesifikatsiyalar backend’da keshlanadi, shuning uchun qoida ro‘yxatdan o‘tkazilishidan oldin qurilgan spesifikatsiya noto‘g‘ri bo‘lib qolaveradi.

def build_fort() -> FastFort:
    register_project_types()      # first statement, not after
    fort = FastFort(...)

Fayl yuklash

file va image — yo‘lni saqlaydigan oddiy satr (string) ustunlari. Baytlar media.root ostiga tushadi; ustun esa yozilgan qiymatni saqlaydi.

Yuklash kartochkasi URL.createObjectURL orqali rasm yoki video preview’i bilan drag-and-drop’ni qo‘llab-quvvatlaydi — va uning ichida haqiqatda yuborishni bajaruvchi narsa sifatida original <input type="file"> saqlanib qoladi, shuning uchun JavaScript o‘chirilgan holda ham ishlayveradi.

Yuklangan fayllar boshqa har qanday view bilan bir xil to‘siq ortida, admin orqali taqdim etiladi. Yuklangan fayl — bu ommaviy resurs emas, boshqa har qanday qator kabi bir yozuv.

Ro‘yxat ustunida

list_displaydagi rasm ustuni saqlangan yo‘l o‘rniga eskiz (thumbnail)ni ko‘rsatadi — chunki yo‘lga tasvirning o‘zi darhol javob beradi. Bu yerda hal qiluvchi omil forma chizadigan boshqaruv elementi hisoblanadi, shuning uchun formfield_overrides orqali rasm sifatida qayta belgilangan oddiy String ham eskiz oladi:

class UserAdmin(admin.ModelAdmin):
    list_display = ("id", "avatar", "email", "full_name")
    formfield_overrides = {"avatar": "image"}

Hali surati bo‘lmagan qatorlar xuddi shunday o‘lchamdagi bo‘sh joy egallovchi (placeholder) oladi, shunda jadval notekis ko‘rinmaydi. Eskizlar lazy tarzda yuklanadi: yuz qatorli sahifa hali hech kim ularga aylantirmasdan turib yuzta so‘rov ochib yubormaydi.

Faqat o‘qish uchun bo‘lganlar

BINARY va SEARCH_VECTOR ko‘rsatiladi, lekin hech qachon yozilmaydi — chunki rastr tasvir yoki to‘liq matnli indeksni kiritish mumkin bo‘lgan hech qanday maydon yo‘q. UNKNOWN ham xuddi shunday: hech narsa tasniflay olmaydigan ustun sahifani ag‘darib tashlash o‘rniga faqat o‘qish uchun qatorga aylanadi.

Oxirgisi — orqasida test turgan hujjatlashtirilgan holat, chunki hech narsa sinamaydigan holat — bu haqiqiy loyiha unga birinchi marta yetib borganda buziladigan holatdir.