Skip to content

Instantly share code, notes, and snippets.

@CoreyMSchafer
Created October 27, 2025 14:08
Show Gist options
  • Select an option

  • Save CoreyMSchafer/26fbfae9fb2ad293cc431530e8932855 to your computer and use it in GitHub Desktop.

Select an option

Save CoreyMSchafer/26fbfae9fb2ad293cc431530e8932855 to your computer and use it in GitHub Desktop.
Pydantic Tutorial Code
import json
from datetime import UTC, datetime
from functools import partial
from typing import Annotated, Literal
from uuid import UUID, uuid4
from pydantic import (
BaseModel,
ConfigDict,
EmailStr,
Field,
HttpUrl,
SecretStr,
ValidationError,
ValidationInfo,
computed_field,
field_validator,
model_validator,
)
class User(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
strict=True,
extra="allow",
frozen=True,
)
uid: UUID = Field(alias="id", default_factory=uuid4)
username: Annotated[str, Field(min_length=3, max_length=20)]
email: EmailStr
password: SecretStr
website: HttpUrl | None = None
age: Annotated[int, Field(ge=13, le=130)]
verified_at: datetime | None = None
bio: str = ""
is_active: bool = True
first_name: str = ""
last_name: str = ""
follower_count: int = 0
@field_validator("username")
@classmethod
def validate_username(cls, v: str) -> str:
if not v.replace("_", "").isalnum():
raise ValueError("Username must be alphanumeric (underscores allowed)")
return v.lower()
@field_validator("website", mode="before")
@classmethod
def add_https(cls, v: str | None) -> str | None:
if v and not v.startswith(("http://", "https://")):
return f"https://{v}"
return v
@computed_field
@property
def display_name(self) -> str:
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
return self.username
@computed_field
@property
def is_influencer(self) -> bool:
return self.follower_count >= 10000
class Comment(BaseModel):
content: str
author_email: EmailStr
likes: int = 0
class BlogPost(BaseModel):
title: Annotated[str, Field(min_length=1, max_length=200)]
content: Annotated[str, Field(min_length=10)]
author: User
view_count: int = 0
is_published: bool = False
tags: list[str] = Field(default_factory=list)
create_at: datetime = Field(default_factory=partial(datetime.now, tz=UTC))
status: Literal["draft", "published", "archived"] = "draft"
slug: Annotated[str, Field(pattern=r"^[a-z0-9-]+$")]
comments: list[Comment] = Field(default_factory=list)
class UserRegistration(BaseModel):
email: EmailStr
password: str
confirm_password: str
@model_validator(mode="after")
def passwords_match(self) -> "UserRegistration":
if self.password != self.confirm_password:
raise ValueError("Passwords do not match")
return self
user_data = {
"id": "3bc4bf25-1b73-44da-9078-f2bb310c7374",
"username": "Corey_Schafer",
"email": "CoreyMSchafer@gmail.com",
"age": 39,
"password": "secret123",
"notes": "Kind've a Karen...",
}
user = User.model_validate_json(json.dumps(user_data))
user.email = "CoreyMSchafer2@gmail.com"
print(user.model_dump_json(indent=2))
### New BlogPost
post = BlogPost(
title="Getting Started with Python",
content="Here's how to begin...",
author_id="12345",
)
### Invalid User
try:
user = User(
uid=0,
username="cs",
email="CoreyMSchafer@gmail.com",
age=12,
)
except ValidationError as e:
print(e)
### Valid User
user = User(
username="coreyms",
email="CoreyMSchafer@gmail.com",
age=39,
password="secret123",
)
print(user)
### Username Validation
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if not v.replace('_', '').isalnum():
raise ValueError('Username must be alphanumeric (underscores allowed)')
return v.lower()
### URL Validation (no mode)
@field_validator('website')
@classmethod
def add_https(cls, v: str | None) -> str | None:
if v and not v.startswith(('http://', 'https://')):
return f'https://{v}'
return v
### Model Validator
class UserRegistration(BaseModel):
email: EmailStr
password: str
confirm_password: str
@model_validator(mode='after')
def passwords_match(self) -> 'UserRegistration':
if self.password != self.confirm_password:
raise ValueError('Passwords do not match')
return self
try:
registration = UserRegistration(
email="CoreyMSchafer@gmail.com",
password="secret123",
confirm_password="secret456"
)
except ValidationError as e:
print(e)
### Computed Fields
first_name: str = ""
last_name: str = ""
follower_count: int = 0
@computed_field
@property
def display_name(self) -> str:
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
return self.username
@computed_field
@property
def is_influencer(self) -> bool:
return self.follower_count >= 10000
### Comment Model
class Comment(BaseModel):
content: str
author_email: EmailStr
likes: int = 0
### BlogPost Dictionary
post_data = {
"title": "Understanding Pydantic Models",
"content": "Pydantic makes data validation easy and intuitive...",
"slug": "understanding-pydantic",
"author": {
"username": "coreyms",
"email": "CoreyMSchafer@gmail.com",
"age": 39,
"password": "secret123",
},
"comments": [
{
"content": "I think I understand nested models now!",
"author_email": "student@example.com",
"likes": 25,
},
{
"content": "Can you cover FastAPI next?",
"author_email": "viewer@example.com",
"likes": 15,
},
],
}
post = BlogPost(**post_data)
print(post.model_dump_json(indent=2))
### User Dictionary
user_data = {
"id": "3bc4bf25-1b73-44da-9078-f2bb310c7374",
"username": "Corey_Schafer",
"email": "CoreyMSchafer@gmail.com",
"age": "39",
"password": "secret123",
}
user = User.model_validate(user_data)
print(user.model_dump_json(indent=2))
@fatihelmina-creator

Copy link
Copy Markdown

import 'package:flutter/material.dart';

void main() {
runApp(const OrganelOyunuApp());
}

class OrganelOyunuApp extends StatelessWidget {
const OrganelOyunuApp({super.key});

@OverRide
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Organel Eşleştirme Oyunu',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
fontFamily: 'Roboto',
),
home: const OyunEkrani(),
);
}
}

// Organel verilerini tutacağımız basit bir model sınıfı
class Organel {
final String isim;
final String gorev;
final IconData ikon;
final Color renk;

Organel({
required this.isim,
required this.gorev,
required this.ikon,
required this.renk,
});
}

class OyunEkrani extends StatefulWidget {
const OyunEkrani({super.key});

@OverRide
State createState() => _OyunEkraniState();
}

class _OyunEkraniState extends State {
// Tüm organellerin ve görevlerinin listesi
final List tumOrganeller = [
Organel(isim: 'Mitokondri', gorev: 'Hücrenin enerji (ATP) santralidir.', ikon: Icons.bolt, renk: Colors.orange),
Organel(isim: 'Ribozom', gorev: 'Protein sentezinden sorumludur.', ikon: Icons.grain, renk: Colors.redAccent),
Organel(isim: 'Kloroplast', gorev: 'Fotosentez yaparak besin ve oksijen üretir.', ikon: Icons.eco, renk: Colors.green),
Organel(isim: 'Çekirdek', gorev: 'Hücrenin yönetim ve kalıtım merkezidir.', ikon: Icons.adjust, renk: Colors.deepPurple),
Organel(isim: 'Lizozom', gorev: 'Hücre içi sindirim yapar.', ikon: Icons.delete_sweep, renk: Colors.brown),
Organel(isim: 'Golgi Aygıtı', gorev: 'Salgı maddelerini üretir ve paketler.', ikon: Icons.waves, renk: Colors.blue),
Organel(isim: 'Koful', gorev: 'Atık maddeleri ve suyu depolar.', ikon: Icons.bubble_chart, renk: Colors.teal),
];

// Doğru eşleştirilen organellerin isimlerini tuttuğumuz liste
List eslesenler = [];

// Sürüklenecek ve hedeflenecek listeleri karıştırmak için
late List suruklenecekOrganeller;
late List hedefGorevler;

@OverRide
void initState() {
super.initState();
oyunuBaslat();
}

// Oyunu sıfırlayan ve listeleri karıştıran fonksiyon
void oyunuBaslat() {
setState(() {
eslesenler.clear();
// Listeleri kopyalayıp rastgele karıştırıyoruz
suruklenecekOrganeller = List.from(tumOrganeller)..shuffle();
hedefGorevler = List.from(tumOrganeller)..shuffle();
});
}

// Oyunun bitip bitmediğini kontrol eden fonksiyon
void kontrolEtOyunBittiMi() {
if (eslesenler.length == tumOrganeller.length) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: const Text('Tebrikler! 🏆', textAlign: TextAlign.center),
content: const Text(
'Tüm hücre organellerini ve görevlerini başarıyla eşleştirdiniz. Artık tam bir biyoloji uzmanısınız!',
textAlign: TextAlign.center,
),
actionsAlignment: MainAxisAlignment.center,
actions: [
ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pop();
oyunuBaslat();
},
icon: const Icon(Icons.refresh),
label: const Text('Yeniden Oyna'),
)
],
),
);
}
}

@OverRide
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: const Text('Hücre Organelleri Eşleştirme', style: TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
actions: [
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'Skor: ${eslesenler.length} / ${tumOrganeller.length}',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
)
],
),
body: Column(
children: [
// Üst Kısım: Sürüklenebilir Organel Çipleri
Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 5))
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Organelleri görevlerin üzerine sürükleyin:',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
Wrap(
alignment: WrapAlignment.center,
spacing: 12.0,
runSpacing: 12.0,
children: suruklenecekOrganeller.map((organel) {
// Eğer organel eşleştirildiyse boş bir kutu göster
if (eslesenler.contains(organel.isim)) {
return Chip(
label: const Text('Eşleşti ✓'),
backgroundColor: Colors.grey[300],
labelStyle: TextStyle(color: Colors.grey[600]),
);
}

                // Eşleştirilmediyse sürüklenebilir Widget'ı göster
                return Draggable<String>(
                  data: organel.isim, // Taşınan veri (organel ismi)
                  feedback: Material( // Sürüklenirken görünen widget
                    color: Colors.transparent,
                    child: Chip(
                      elevation: 8,
                      backgroundColor: organel.renk,
                      label: Text(organel.isim, style: const TextStyle(color: Colors.white, fontSize: 18)),
                      avatar: Icon(organel.ikon, color: Colors.white),
                    ),
                  ),
                  childWhenDragging: Chip( // Sürüklenirken asıl yerinde görünen widget
                    backgroundColor: organel.renk.withOpacity(0.3),
                    label: Text(organel.isim, style: const TextStyle(color: Colors.transparent)),
                  ),
                  child: Chip( // Normal durumda görünen widget
                    elevation: 2,
                    backgroundColor: organel.renk,
                    label: Text(organel.isim, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
                    avatar: Icon(organel.ikon, color: Colors.white),
                  ),
                );
              }).toList(),
            ),
          ],
        ),
      ),
      
      // Alt Kısım: Görev Hedefleri Listesi
      Expanded(
        child: ListView.builder(
          padding: const EdgeInsets.all(16.0),
          itemCount: hedefGorevler.length,
          itemBuilder: (context, index) {
            final hedef = hedefGorevler[index];
            final eslestiMi = eslesenler.contains(hedef.isim);

            return DragTarget<String>(
              // Üzerine bırakılan veriyi kabul edip etmeme kuralı
              onWillAcceptWithDetails: (details) => !eslestiMi, 
              
              // Veri başarıyla bırakıldığında çalışır
              onAcceptWithDetails: (details) {
                if (details.data == hedef.isim) {
                  setState(() {
                    eslesenler.add(hedef.isim);
                  });
                  kontrolEtOyunBittiMi();
                } else {
                  // Hatalı eşleştirmede kullanıcıya bilgi ver
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text('Yanlış! ${details.data} bu görevi yapmaz.'),
                      backgroundColor: Colors.red,
                      duration: const Duration(seconds: 1),
                    ),
                  );
                }
              },
              builder: (context, candidateData, rejectedData) {
                // Hedef kartının görünümü
                return Card(
                  margin: const EdgeInsets.only(bottom: 12.0),
                  elevation: eslestiMi ? 1 : (candidateData.isNotEmpty ? 8 : 2),
                  color: eslestiMi 
                      ? hedef.renk.withOpacity(0.1) 
                      : (candidateData.isNotEmpty ? Colors.blue[50] : Colors.white),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12),
                    side: BorderSide(
                      color: eslestiMi ? hedef.renk : (candidateData.isNotEmpty ? Colors.blue : Colors.transparent),
                      width: 2,
                    ),
                  ),
                  child: ListTile(
                    contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                    leading: Container(
                      width: 50,
                      height: 50,
                      decoration: BoxDecoration(
                        color: eslestiMi ? hedef.renk : Colors.grey[200],
                        shape: BoxShape.circle,
                      ),
                      child: Icon(
                        eslestiMi ? hedef.ikon : Icons.help_outline,
                        color: eslestiMi ? Colors.white : Colors.grey[500],
                      ),
                    ),
                    title: Text(
                      hedef.gorev,
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: eslestiMi ? FontWeight.bold : FontWeight.normal,
                        color: Colors.black87,
                      ),
                    ),
                    subtitle: eslestiMi
                        ? Text(hedef.isim, style: TextStyle(color: hedef.renk, fontWeight: FontWeight.bold, fontSize: 16))
                        : const Text('Buraya sürükleyin', style: TextStyle(fontStyle: FontStyle.italic)),
                  ),
                );
              },
            );
          },
        ),
      ),
    ],
  ),
  // Oyunu istediğiniz zaman yeniden başlatmak için buton
  floatingActionButton: FloatingActionButton(
    onPressed: oyunuBaslat,
    tooltip: 'Oyunu Sıfırla',
    child: const Icon(Icons.refresh),
  ),
);

}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment