# 👨‍💻 Developer Quick Reference Guide

## Setup & Running

### 1. Install Dependencies
```bash
pip install django==4.0.1
pip install psycopg2-binary          # PostgreSQL adapter
pip install django-ckeditor
pip install Pillow                   # Image processing
```

### 2. Database Setup
```bash
# Create PostgreSQL database
createdb bmc
createuser admin

# Apply migrations
python manage.py migrate

# Create superuser (admin)
python manage.py createsuperuser
```

### 3. Run Development Server
```bash
python manage.py runserver
# Access at: http://localhost:8000
# Admin: http://localhost:8000/admin
```

---

## Common Tasks

### Add New Product Type
**File**: `BmcBase/models.py`

**Current Issue**: Product type is hardcoded string
```python
# Currently:
class SanPham(models.Model):
    loai = models.CharField(max_length=100)  # "thuốc sâu", "thuốc cỏ", etc.
    # ...
```

**Better Solution**: Create a ProductType model
```python
class ProductType(models.Model):
    ten = models.CharField(max_length=100, unique=True)
    ten_en = models.CharField(max_length=100)
    
class SanPham(models.Model):
    loai = models.ForeignKey(ProductType, on_delete=models.SET_NULL, null=True)
    # ...
```

---

### Add Blog Post
1. Go to `/admin/BmcBase/kienthuc/`
2. Click "Add KienThuc"
3. Fill:
   - **ten**: Vietnamese title
   - **ten_tieng_anh**: English title
   - **ngay_dang**: Publish date
   - **the_loai**: Select category (can select multiple)
   - **mo_ta**: Short summary
   - **mo_ta_tieng_anh**: English summary
   - **noi_dung**: Full content (use CKEditor)
   - **noi_dung_tieng_anh**: English content
   - **banner**: Upload featured image
4. Click "Save"
5. Post goes live immediately

---

### Approve Comments
1. Go to `/admin/BmcBase/binhluan/`
2. See list of comments with approval status
3. Check box in `trang_thai_da_duyet` column
4. Comments appear on blog post automatically

---

### Add Job Posting
1. Go to `/admin/BmcBase/careers/`
2. Add new career:
   - **tieu_de**: Job title (VN)
   - **tieu_de_en**: Job title (EN)
   - **ngay_dang**: Post date
   - **noi_dung_cong_viec**: Full job description
   - **noi_dung_cong_viec_en**: English description
   - **muc_luong_min**: Min salary (can be text)
   - **muc_luong_max**: Max salary
3. Appears at `/careers/` immediately

---

## Views Deep Dive

### View Pattern Used
All views are **function-based views (FBV)** using the pattern:
```python
def view_name(request, param1=None, param2=None):
    # 1. Get data from DB
    objects = Model.objects.filter(...)
    
    # 2. Handle forms (if POST)
    if request.method == 'POST':
        form = MyForm(request.POST, request.FILES)
        if form.is_valid():
            # Process form
            pass
    else:
        form = MyForm()
    
    # 3. Build context
    context = {
        'key': value,
    }
    
    # 4. Render template
    return render(request, 'template.html', context)
```

### Key Methods to Know

#### QuerySet Methods
```python
# Get all
objects = Model.objects.all()

# Filter
objects = Model.objects.filter(status=True)

# Get first match
obj = Model.objects.filter(...).first()

# Get by ID
obj = Model.objects.get(pk=1)

# Or 404 error
obj = get_object_or_404(Model, pk=1)

# Filter with Q (OR)
from django.db.models import Q
results = Model.objects.filter(Q(field1=val1) | Q(field2=val2))

# Order by
objects = Model.objects.all().order_by('-created_date')

# Pagination
from django.core.paginator import Paginator
paginator = Paginator(objects, 12)  # 12 per page
page = paginator.get_page(request.GET.get('page'))
```

#### Template Tags
```django
{% load static %}
{% load i18n %}

{% block content %}
{% endblock %}

{% for item in items %}
    {{ item.name }}
{% empty %}
    No items
{% endfor %}

{% if condition %}
    <p>True</p>
{% else %}
    <p>False</p>
{% endif %}

{% get_current_language as lang %}

{% trans "Translate this" %}

{% url 'view_name' arg1 arg2 %}

{% static 'css/style.css' %}
```

---

## File Upload Safety

### How File Uploads Work
1. User submits form with file
2. Django handles multipart/form-data
3. File stored in `/media/contacts/` with safe name:
   - Original: `Báo cáo.pdf` → Safe: `bao-cao-a1b2c3d4e5.pdf`
   - Original filename stored in `original_file_name` field

### Safe Upload Implementation
```python
def _build_safe_upload_name(uploaded_file):
    """Generate safe filename to prevent directory traversal"""
    original_name = uploaded_file.name or "uploaded-file"
    base_name, extension = os.path.splitext(original_name)
    safe_base_name = slugify(base_name)  # Remove special chars
    if not safe_base_name:
        safe_base_name = "uploaded-file"
    unique_suffix = uuid.uuid4().hex[:10]  # Prevent collisions
    return f"{safe_base_name}-{unique_suffix}{extension.lower()}"
```

---

## Search Implementation

### Full-Text Search (Blog)
Uses **PostgreSQL Full-Text Search**:
```python
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank

# Build search across multiple fields
search_vector = SearchVector("ten", "ten_tieng_anh", "noi_dung", "noi_dung_tieng_anh")
search_query = SearchQuery("keyword")

# Annotate with rank and filter
results = KienThuc.objects.annotate(
    search=search_vector,
    rank=SearchRank(search_vector, search_query)
).filter(search=search_query).order_by("-rank")
```

**Advantages**:
- ✅ Full-text (not just substring)
- ✅ Multilingual support
- ✅ Ranking by relevance
- ✅ Fast on large datasets

---

## Common Bugs & Fixes

### Bug: Comments Not Showing
**Check**:
1. Comment `trang_thai_da_duyet` is `True` in admin?
2. Comment associated with correct post?

```python
# In template (safe check)
{% for comment in comments %}
    {% if comment.trang_thai_da_duyet %}
        {{ comment.noi_dung }}
    {% endif %}
{% endfor %}
```

### Bug: Images Not Uploading
**Check**:
1. `/media/` directory exists and writable?
2. DEBUG=True for development?
3. MEDIA_URL and MEDIA_ROOT configured?

```python
# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# urls.py (development only)
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

### Bug: Pagination Returns Wrong Page
```python
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

try:
    page = paginator.get_page(page_number)
except PageNotAnInteger:
    page = paginator.get_page(1)  # First page
except EmptyPage:
    page = paginator.get_page(paginator.num_pages)  # Last page
```

---

## Database Tips

### Useful Queries (via manage.py shell)
```bash
python manage.py shell
```

```python
# Get product by ID
p = SanPham.objects.get(pk=1)

# Get all products of type "thuốc sâu"
products = SanPham.objects.filter(loai="thuốc sâu")

# Get blog posts by category
category = TheLoaiBaiViet.objects.get(pk=1)
posts = KienThuc.objects.filter(the_loai=category)

# Get approved comments
comments = BinhLuan.objects.filter(trang_thai_da_duyet=True)

# Count items
count = SanPham.objects.count()

# Order and limit
latest = SanPham.objects.all().order_by('-id')[:9]

# Delete (CAREFUL!)
SanPham.objects.filter(loai="old").delete()
```

---

## Adding New View

### Step 1: Write View Function
**File**: `BmcBase/views.py`

```python
def my_new_view(request, id=None):
    # Get data
    data = MyModel.objects.get(pk=id)
    
    # Build context
    context = {'data': data}
    
    # Render
    return render(request, 'BmcBase/my_template.html', context)
```

### Step 2: Create Template
**File**: `BmcBase/templates/BmcBase/my_template.html`

```html
{% extends 'BmcBase/base.html' %}
{% load static %}

{% block content %}
<div class="container">
    <h1>{{ data.title }}</h1>
    <p>{{ data.description }}</p>
</div>
{% endblock %}
```

### Step 3: Add URL Route
**File**: `BmcBase/urls.py`

```python
path('my-page/<int:id>/', views.my_new_view, name='my_new_view'),
```

### Step 4: Link in Template
```html
<a href="{% url 'my_new_view' object.id %}">View Details</a>
```

---

## Performance Tips

### 1. Use select_related for ForeignKey
```python
# Slow (N+1 query)
for comment in BinhLuan.objects.all():
    print(comment.bai_viet.ten)  # Extra query per comment!

# Fast (single query with JOIN)
for comment in BinhLuan.objects.select_related('bai_viet'):
    print(comment.bai_viet.ten)  # No extra queries
```

### 2. Use prefetch_related for M2M
```python
# Slow
for post in KienThuc.objects.all():
    categories = post.the_loai.all()  # Extra query per post!

# Fast (single query per relationship)
for post in KienThuc.objects.prefetch_related('the_loai'):
    categories = post.the_loai.all()  # Cached
```

### 3. Add Indexes
```python
class SanPham(models.Model):
    loai = models.CharField(max_length=100, db_index=True)  # Index on type
    ngay_tao = models.DateField(db_index=True)              # Index on date
```

### 4. Limit Queryset
```python
# Bad: Load all 10,000 products
products = SanPham.objects.all()

# Good: Load only what you need
products = SanPham.objects.all()[:9]  # Only 9
```

---

## Debugging

### 1. Print to Console
```python
print("Debug value:", variable)
```

### 2. Django Debug Toolbar (add to dev)
```bash
pip install django-debug-toolbar
```

### 3. Logging
```python
import logging
logger = logging.getLogger(__name__)

logger.debug("This is debug info")
logger.info("This is info")
logger.error("This is an error")
```

### 4. Shell Testing
```bash
python manage.py shell

from BmcBase.models import *
p = SanPham.objects.first()
print(p)
print(p.hinh_anh.url)
```

---

## Git Workflow (if applicable)

```bash
# See status
git status

# Add changes
git add .

# Commit
git commit -m "Feature: add new blog search"

# Push
git push origin main

# Create branch for feature
git checkout -b feature/new-feature
git push -u origin feature/new-feature
```

---

## Security Checklist

- [ ] DEBUG = False in production
- [ ] SECRET_KEY not hardcoded (use .env)
- [ ] Database password in .env
- [ ] ALLOWED_HOSTS properly set
- [ ] Use HTTPS in production
- [ ] Escape user input in templates (Django auto-escapes)
- [ ] Use Django forms (CSRF protection)
- [ ] Validate file uploads
- [ ] Run `python manage.py check --deploy`

---

## Contact Developers

For questions about:
- **Django/Python**: Check Django docs
- **CKEditor**: See CKEditor docs
- **PostgreSQL**: Check DB admin or Django ORM docs
- **Project-specific**: Ask project owner

**Project Owner**: Huy Nguyen (khabmcit@gmail.com)

---

**Version**: 1.0  
**Last Updated**: 18/04/2026
