Skip to content

Commit bbfdb84

Browse files
committedMar 15, 2025
add all file project
0 parents  commit bbfdb84

29 files changed

+1135
-0
lines changed
 

‎.gitignore

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
share/python-wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# PyInstaller
30+
# Usually these files are written by a python script from a template
31+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
32+
*.manifest
33+
*.spec
34+
35+
# Installer logs
36+
pip-log.txt
37+
pip-delete-this-directory.txt
38+
39+
# Unit test / coverage reports
40+
htmlcov/
41+
.tox/
42+
.nox/
43+
.coverage
44+
.coverage.*
45+
.cache
46+
nosetests.xml
47+
coverage.xml
48+
*.cover
49+
*.py,cover
50+
.hypothesis/
51+
.pytest_cache/
52+
cover/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
.pybuilder/
76+
target/
77+
78+
# Jupyter Notebook
79+
.ipynb_checkpoints
80+
81+
# IPython
82+
profile_default/
83+
ipython_config.py
84+
85+
# pyenv
86+
# For a library or package, you might want to ignore these files since the code is
87+
# intended to run in multiple environments; otherwise, check them in:
88+
# .python-version
89+
90+
# pipenv
91+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94+
# install all needed dependencies.
95+
#Pipfile.lock
96+
97+
# UV
98+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99+
# This is especially recommended for binary packages to ensure reproducibility, and is more
100+
# commonly ignored for libraries.
101+
#uv.lock
102+
103+
# poetry
104+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105+
# This is especially recommended for binary packages to ensure reproducibility, and is more
106+
# commonly ignored for libraries.
107+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108+
#poetry.lock
109+
110+
# pdm
111+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112+
#pdm.lock
113+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114+
# in version control.
115+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116+
.pdm.toml
117+
.pdm-python
118+
.pdm-build/
119+
120+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121+
__pypackages__/
122+
123+
# Celery stuff
124+
celerybeat-schedule
125+
celerybeat.pid
126+
127+
# SageMath parsed files
128+
*.sage.py
129+
130+
# Environments
131+
.env
132+
.venv
133+
env/
134+
venv/
135+
ENV/
136+
env.bak/
137+
venv.bak/
138+
139+
# Spyder project settings
140+
.spyderproject
141+
.spyproject
142+
143+
# Rope project settings
144+
.ropeproject
145+
146+
# mkdocs documentation
147+
/site
148+
149+
# mypy
150+
.mypy_cache/
151+
.dmypy.json
152+
dmypy.json
153+
154+
# Pyre type checker
155+
.pyre/
156+
157+
# pytype static type analyzer
158+
.pytype/
159+
160+
# Cython debug symbols
161+
cython_debug/
162+
163+
# PyCharm
164+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166+
# and can be added to the global gitignore or merged into this file. For a more nuclear
167+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
168+
#.idea/
169+
170+
# Ruff stuff:
171+
.ruff_cache/
172+
173+
# PyPI configuration file
174+
.pypirc

‎LICENSE.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 masud ghasmi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎README.md

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# BlogMasterDjangoExercise
2+
3+
**English** | [فارسی](#فارسی)
4+
5+
## Introduction
6+
This project is a simple yet powerful blog built using the Django framework. The goal is to provide a platform for creating, editing, and displaying articles using Django best practices. This README includes complete steps for installation, setup, usage, testing, and development.
7+
8+
## Prerequisites
9+
Ensure your system has:
10+
- **Python 3.6** or higher
11+
- **pip** (Python package manager)
12+
- (Optional) **virtualenv** for isolated environments
13+
14+
## Installation & Setup
15+
16+
### 1. Clone the Repository
17+
Clone the source code from GitHub:
18+
```bash
19+
git clone https://github.com/YourUsername/YourProjectName.git
20+
cd YourProjectName
21+
```
22+
23+
### 2. Create a Virtual Environment (Optional but Recommended)
24+
```bash
25+
python -m venv venv
26+
```
27+
Activate the virtual environment:
28+
- **Windows:**
29+
```bash
30+
venv\Scripts\activate
31+
```
32+
- **Linux/Mac:**
33+
```bash
34+
source venv/bin/activate
35+
```
36+
37+
### 3. Install Dependencies
38+
Install packages from `requirements.txt`:
39+
```bash
40+
pip install -r requirements.txt
41+
```
42+
43+
### 4. Apply Migrations
44+
Set up the database:
45+
```bash
46+
python manage.py migrate
47+
```
48+
49+
### 5. Create a Superuser (Optional)
50+
```bash
51+
python manage.py createsuperuser
52+
```
53+
Follow the prompts to set up a username, email, and password.
54+
55+
### 6. Run the Development Server
56+
```bash
57+
python manage.py runserver
58+
```
59+
Visit [http://127.0.0.1:8000](http://127.0.0.1:8000) in your browser.
60+
61+
## Project Structure
62+
```
63+
- mysite/ # Main project folder
64+
- settings.py # Project settings
65+
- urls.py # Main URL configurations
66+
- blog/ # Blog app
67+
- admin.py # Admin panel registration
68+
- models.py # Database models (articles, categories)
69+
- templates/ # HTML templates
70+
- static/ # CSS, JS, images
71+
- migrations/ # Database migration files
72+
- db.sqlite3 # Default SQLite database (development)
73+
- manage.py # Django management tool
74+
```
75+
76+
## Usage
77+
78+
### View the Blog
79+
Visit [http://127.0.0.1:8000](http://127.0.0.1:8000) to see the blog.
80+
81+
### Access the Admin Panel
82+
1. Go to [http://127.0.0.1:8000/admin](http://127.0.0.1:8000/admin).
83+
2. Log in with your superuser credentials.
84+
85+
### Create/Edit Articles
86+
- Use the admin panel to manage articles.
87+
- Public pages display articles and their details.
88+
89+
## Development Notes
90+
91+
### Model Changes
92+
After modifying models:
93+
```bash
94+
python manage.py makemigrations
95+
python manage.py migrate
96+
```
97+
98+
### Static Files (Production)
99+
```bash
100+
python manage.py collectstatic
101+
```
102+
103+
### Environment Settings
104+
Modify `mysite/settings.py` for database, email, etc.
105+
106+
## License
107+
MIT License
108+
109+
## Contact
110+
- Created with ❤️ by [Sorna](https://github.com/sorna-fast)
111+
- Email: masudpythongit@gmail.com
112+
113+
---
114+
115+
# فارسی
116+
117+
## مقدمه
118+
این پروژه یک وبلاگ ساده و قدرتمند با استفاده از فریمورک Django است. هدف، ایجاد بستری برای تولید، ویرایش و نمایش مقالات با روش‌های بهینه جنگو می‌باشد. این فایل شامل مراحل نصب، راه‌اندازی، استفاده، تست و توسعه پروژه است.
119+
120+
## پیش‌نیازها
121+
- **پایتون 3.6** یا بالاتر
122+
- **pip** (مدیر بسته‌های پایتون)
123+
- (اختیاری) **virtualenv** برای محیط‌های مجزا
124+
125+
## نصب و راه‌اندازی
126+
127+
### 1. کلون کردن مخزن
128+
```bash
129+
git clone https://github.com/YourUsername/YourProjectName.git
130+
cd YourProjectName
131+
```
132+
133+
### 2. ایجاد محیط مجازی (اختیاری)
134+
```bash
135+
python -m venv venv
136+
```
137+
فعالسازی محیط:
138+
- **ویندوز:**
139+
```bash
140+
venv\Scripts\activate
141+
```
142+
- **لینوکس/مک:**
143+
```bash
144+
source venv/bin/activate
145+
```
146+
147+
### 3. نصب بسته‌ها
148+
```bash
149+
pip install -r requirements.txt
150+
```
151+
152+
### 4. اعمال مهاجرت‌ها
153+
```bash
154+
python manage.py migrate
155+
```
156+
157+
### 5. ایجاد سوپر کاربر (اختیاری)
158+
```bash
159+
python manage.py createsuperuser
160+
```
161+
162+
### 6. اجرای سرور توسعه
163+
```bash
164+
python manage.py runserver
165+
```
166+
به آدرس [http://127.0.0.1:8000](http://127.0.0.1:8000) مراجعه کنید.
167+
168+
## ساختار پروژه
169+
```
170+
- mysite/ # پوشه اصلی پروژه
171+
- settings.py # تنظیمات پروژه
172+
- urls.py # تنظیمات URL اصلی
173+
- blog/ # اپلیکیشن وبلاگ
174+
- admin.py # ثبت مدل‌ها در پنل مدیریت
175+
- models.py # مدل‌های پایگاه داده (مقالات، دسته‌بندی‌ها)
176+
- templates/ # قالب‌های HTML
177+
- static/ # فایل‌های استاتیک
178+
- migrations/ # فایل‌های مهاجرت
179+
- db.sqlite3 # پایگاه داده پیش‌فرش SQLite
180+
- manage.py # ابزار مدیریت جنگو
181+
```
182+
183+
## نحوه استفاده
184+
185+
### مشاهده وبلاگ
186+
به آدرس [http://127.0.0.1:8000](http://127.0.0.1:8000) بروید.
187+
188+
### پنل مدیریت
189+
1. آدرس [http://127.0.0.1:8000/admin](http://127.0.0.1:8000/admin).
190+
2. با حساب سوپر کاربر وارد شوید.
191+
192+
### مدیریت مقالات
193+
- از پنل مدیریت برای ایجاد یا ویرایش مقالات استفاده کنید.
194+
- صفحات عمومی، مقالات را نمایش می‌دهند.
195+
196+
## نکات توسعه
197+
198+
### تغییر مدل‌ها
199+
پس از تغییر مدل‌ها:
200+
```bash
201+
python manage.py makemigrations
202+
python manage.py migrate
203+
```
204+
205+
### فایل‌های استاتیک (محیط تولید)
206+
```bash
207+
python manage.py collectstatic
208+
```
209+
210+
### تنظیمات محیطی
211+
ویرایش `mysite/settings.py` برای پایگاه داده، ایمیل و غیره.
212+
213+
## مجوز
214+
مجوز MIT
215+
216+
## ارتباط با توسعه‌دهنده
217+
- ساخته شده با ❤️ توسط [Sorna](https://github.com/sorna-fast)
218+
- ایمیل: masudpythongit@gmail.com

‎blog/__init__.py

Whitespace-only changes.

‎blog/admin.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from django.contrib import admin
2+
from . import models
3+
4+
5+
class PostAdmin(admin.ModelAdmin):
6+
list_display = ('title', 'author', 'created_date', 'status',)
7+
list_filter = ('title', 'author', 'status',)
8+
search_fields = ('title',)
9+
10+
11+
class CommentAdmin(admin.ModelAdmin):
12+
list_display = ('name', 'email', 'created_date', 'active',)
13+
list_filter = ('name', 'email', 'active',)
14+
search_fields = ('comment',)
15+
16+
17+
admin.site.register(models.Post, PostAdmin)
18+
admin.site.register(models.Comment, CommentAdmin)

‎blog/apps.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class BlogConfig(AppConfig):
5+
name = 'blog'

‎blog/forms.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from django import forms
2+
from . import models
3+
4+
5+
class CommentForm(forms.ModelForm):
6+
class Meta:
7+
model = models.Comment
8+
fields = ('name', 'email', 'comment',)

‎blog/migrations/0001_initial.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 3.1.7 on 2021-03-15 06:10
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
initial = True
11+
12+
dependencies = [
13+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name='Post',
19+
fields=[
20+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21+
('title', models.CharField(max_length=250, unique=True)),
22+
('slug', models.SlugField(max_length=250, unique=True)),
23+
('body', models.TextField()),
24+
('created_date', models.DateTimeField(auto_now_add=True)),
25+
('status', models.CharField(choices=[('draft', 'Draft'), ('published', 'Published')], default='draft', max_length=10)),
26+
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
27+
],
28+
),
29+
]

‎blog/migrations/0002_comment.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 3.1.7 on 2021-03-16 09:38
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('blog', '0001_initial'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='Comment',
16+
fields=[
17+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('name', models.CharField(max_length=100)),
19+
('email', models.EmailField(max_length=254)),
20+
('comment', models.CharField(max_length=500)),
21+
('active', models.BooleanField(default=False)),
22+
('created_date', models.DateTimeField(auto_now_add=True)),
23+
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.post')),
24+
],
25+
),
26+
]

‎blog/migrations/0003_post_tags.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 3.1.7 on 2021-03-16 14:03
2+
3+
from django.db import migrations
4+
import taggit.managers
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('taggit', '0003_taggeditem_add_unique_index'),
11+
('blog', '0002_comment'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='post',
17+
name='tags',
18+
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
19+
),
20+
]

‎blog/migrations/__init__.py

Whitespace-only changes.

‎blog/models.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from django.db import models
2+
from django.contrib.auth.models import User
3+
from django.shortcuts import reverse
4+
from taggit.managers import TaggableManager
5+
6+
7+
class Post(models.Model):
8+
STATUS_CHOICES = (
9+
('draft', 'Draft'),
10+
('published', 'Published'),
11+
)
12+
13+
title = models.CharField(max_length=250, unique=True)
14+
slug = models.SlugField(max_length=250, unique=True)
15+
author = models.ForeignKey(User, on_delete=models.CASCADE)
16+
body = models.TextField()
17+
created_date = models.DateTimeField(auto_now_add=True)
18+
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
19+
tags = TaggableManager()
20+
21+
def __str__(self):
22+
return self.title
23+
24+
def get_absolute_url(self):
25+
return reverse('post_detail', args=[self.created_date.year,
26+
self.created_date.strftime('%m'),
27+
self.created_date.strftime('%d'),
28+
self.slug])
29+
30+
def active_comment(self):
31+
return self.comment_set.filter(active=True)
32+
33+
34+
class Comment(models.Model):
35+
post = models.ForeignKey(Post, on_delete=models.CASCADE)
36+
name = models.CharField(max_length=100)
37+
email = models.EmailField()
38+
comment = models.CharField(max_length=500)
39+
active = models.BooleanField(default=False)
40+
created_date = models.DateTimeField(auto_now_add=True)
41+
42+
def __str__(self):
43+
return "{} by {}".format(self.comment, self.name)

‎blog/static/css/styles.css

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
:root {
2+
--primary-color: #2c3e50;
3+
--secondary-color: #3498db;
4+
--accent-color: #e74c3c;
5+
--light-grey: #ecf0f1;
6+
--dark-grey: #7f8c8d;
7+
--border-radius: 8px;
8+
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
9+
}
10+
11+
* {
12+
box-sizing: border-box;
13+
margin: 0;
14+
padding: 0;
15+
}
16+
17+
html {
18+
font-size: 16px;
19+
scroll-behavior: smooth;
20+
height: 100%;
21+
}
22+
23+
body {
24+
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
25+
line-height: 1.6;
26+
color: var(--primary-color);
27+
background-color: #f8f9fa;
28+
min-height: 100%;
29+
display: flex;
30+
flex-direction: column;
31+
}
32+
33+
a {
34+
color: var(--secondary-color);
35+
text-decoration: none;
36+
transition: color 0.3s ease;
37+
}
38+
39+
a:hover {
40+
color: var(--accent-color);
41+
text-decoration: underline;
42+
}
43+
44+
h1 {
45+
font-size: 2.5rem;
46+
margin-bottom: 1.5rem;
47+
padding-bottom: 1rem;
48+
border-bottom: 2px solid var(--light-grey);
49+
position: relative;
50+
letter-spacing: -0.5px;
51+
}
52+
53+
h1::after {
54+
content: '';
55+
position: absolute;
56+
bottom: -2px;
57+
left: 0;
58+
width: 50px;
59+
height: 2px;
60+
background: var(--secondary-color);
61+
}
62+
63+
.main-content {
64+
display: flex;
65+
flex: 1;
66+
gap: 2rem;
67+
max-width: 1200px;
68+
margin: 0 auto;
69+
padding: 2rem;
70+
}
71+
72+
#container {
73+
flex: 3;
74+
background: white;
75+
padding: 2rem;
76+
border-radius: var(--border-radius);
77+
box-shadow: var(--box-shadow);
78+
}
79+
80+
#sidebar {
81+
flex: 1;
82+
background: linear-gradient(145deg, var(--light-grey) 0%, #dfe6e9 100%);
83+
padding: 2rem;
84+
border-radius: var(--border-radius);
85+
box-shadow: var(--box-shadow);
86+
height: fit-content;
87+
}
88+
89+
.date {
90+
color: var(--dark-grey);
91+
font-size: 0.9rem;
92+
display: block;
93+
margin-bottom: 0.5rem;
94+
}
95+
96+
.post-preview {
97+
background: white;
98+
padding: 1.5rem;
99+
margin-bottom: 1.5rem;
100+
border-radius: var(--border-radius);
101+
transition: transform 0.3s ease;
102+
}
103+
104+
.post-preview:hover {
105+
transform: translateY(-3px);
106+
box-shadow: var(--box-shadow);
107+
}
108+
109+
.pagination {
110+
display: flex;
111+
gap: 1rem;
112+
justify-content: center;
113+
margin-top: 2rem;
114+
padding: 1.5rem;
115+
background: white;
116+
border-radius: var(--border-radius);
117+
}
118+
119+
.page-item {
120+
padding: 0.5rem 1rem;
121+
border-radius: 5px;
122+
background: var(--light-grey);
123+
transition: all 0.3s ease;
124+
}
125+
126+
.page-item.active {
127+
background: var(--secondary-color);
128+
color: white;
129+
}
130+
131+
.page-item:hover:not(.active) {
132+
background: #bdc3c7;
133+
}
134+
135+
.sidebar-widget {
136+
margin-bottom: 2rem;
137+
}
138+
139+
.sidebar-widget h3 {
140+
font-size: 1.2rem;
141+
margin-bottom: 1rem;
142+
color: var(--primary-color);
143+
padding-bottom: 0.5rem;
144+
border-bottom: 2px solid var(--secondary-color);
145+
}
146+
147+
.tag-cloud {
148+
display: flex;
149+
flex-wrap: wrap;
150+
gap: 0.5rem;
151+
}
152+
153+
.tag {
154+
background: var(--secondary-color);
155+
color: white !important;
156+
padding: 0.3rem 0.8rem;
157+
border-radius: 20px;
158+
font-size: 0.9rem;
159+
transition: all 0.3s ease;
160+
}
161+
162+
.tag:hover {
163+
background: var(--accent-color);
164+
transform: scale(1.05);
165+
}
166+
167+
@media (max-width: 768px) {
168+
.main-content {
169+
flex-direction: column;
170+
padding: 1rem;
171+
}
172+
173+
h1 {
174+
font-size: 2rem;
175+
}
176+
177+
#container,
178+
#sidebar {
179+
width: 100%;
180+
margin-bottom: 1rem;
181+
}
182+
}

‎blog/templates/base.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html>
2+
{% load static %}
3+
{% load my_tags %}
4+
<html lang="en">
5+
<head>
6+
<meta charset="UTF-8">
7+
<title>
8+
{% block title %}
9+
Base
10+
{% endblock %}
11+
</title>
12+
<link href="{% static 'css/styles.css' %}" rel="stylesheet">
13+
</head>
14+
<body>
15+
<div id="container">
16+
{% block content %}
17+
{% endblock %}
18+
19+
<h1>Comments:</h1>
20+
{% block comment %}
21+
22+
{% endblock %}
23+
</div>
24+
<div id="sidebar">
25+
<h2>My blog ({% total_count %})</h2>
26+
<a href="{% url 'index' %}">Home</a>
27+
<br />
28+
<a href="{% url 'contact_us'%}">Contact Us</a>
29+
</div>
30+
31+
</body>
32+
</html>

‎blog/templates/contact_us.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{% extends 'base.html' %}
2+
3+
{% block title %}
4+
Contact US
5+
{% endblock %}
6+
7+
{% block content %}
8+
<h1>Contact US</h1>
9+
10+
<h3>Contact US: masudpythongit@gmail.com </h3>
11+
<h3>github :<a href="https://github.com/sorna-fast"> sorna-fast</a> </h3>
12+
{% endblock %}

‎blog/templates/post_detail.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{% extends 'base.html' %}
2+
3+
{% block title %}
4+
{{ post.title }}
5+
{% endblock %}
6+
7+
{% block content %}
8+
<h1>{{ post.title }} Detail:</h1>
9+
<p>Author: {{ post.author }}</p>
10+
<p>{{ post.body }}</p>
11+
<hr />
12+
<p>Created Time: {{ post.created_date }}</p>
13+
{% endblock %}
14+
15+
{% block comment %}
16+
{% if new_comment %}
17+
<p>نظر شما در انتظار تایید است</p>
18+
{% else %}
19+
<form method="post">
20+
{{ comment_form.as_p }}
21+
{% csrf_token %}
22+
<input type="submit" value="Send Comment">
23+
</form>
24+
{% endif %}
25+
26+
{% for comment in post.active_comment %}
27+
<p>name: {{ comment.name }}</p>
28+
<p>email: {{ comment.email }}</p>
29+
<p>comment: {{ comment.comment }}</p>
30+
<hr />
31+
{% endfor %}
32+
{% endblock %}

‎blog/templates/post_list.html

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{% extends 'base.html' %}
2+
{% load my_tags %}
3+
4+
{% block title %}
5+
Post List
6+
{% endblock %}
7+
8+
{% block content %}
9+
<h1>Post List:</h1>
10+
{% for post in page_obj %}
11+
<div>
12+
<h3>
13+
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
14+
</h3>
15+
16+
{% tag_label post.tags.all.count %}
17+
{% for tag in post.tags.all %}
18+
<a href="{% url 'index_tag' tag.slug %}">
19+
{{ tag.slug }}
20+
</a>
21+
22+
{% if not forloop.last %}, {% endif %}
23+
{% endfor %}
24+
25+
</div>
26+
27+
{% endfor %}
28+
29+
<div class="pagination">
30+
<span class="step-links">
31+
{% if page_obj.has_previous %}
32+
<a href="?page=1">&laquo; first</a>
33+
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
34+
{% endif %}
35+
36+
<span class="current">
37+
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
38+
</span>
39+
40+
{% if page_obj.has_next %}
41+
<a href="?page={{ page_obj.next_page_number }}">next</a>
42+
<a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
43+
{% endif %}
44+
</span>
45+
</div>
46+
{% endblock %}

‎blog/templatetags/__init__.py

Whitespace-only changes.

‎blog/templatetags/my_tags.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from django import template
2+
from ..models import Post
3+
4+
5+
register = template.Library()
6+
7+
8+
@register.simple_tag
9+
def total_count():
10+
return Post.objects.filter(status='published').count()
11+
12+
13+
@register.simple_tag
14+
def tag_label(tag_count):
15+
if tag_count > 1:
16+
return 'Tags: '
17+
else:
18+
return 'Tag: '

‎blog/tests.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.test import TestCase
2+
3+
# Create your tests here.

‎blog/urls.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.urls import path
2+
from . import views
3+
4+
urlpatterns = [
5+
path('', views.post_list, name="index"),
6+
path('<str:tag_slug>/', views.post_list, name="index_tag"),
7+
path('<int:year>/<int:month>/<int:day>/<str:slug>/', views.post_detail, name="post_detail"),
8+
path('/contact_us/', views.contact_us, name="contact_us")
9+
]

‎blog/views.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from django.http import HttpResponse
2+
from django.shortcuts import render, get_object_or_404
3+
from . import models
4+
from django.core.paginator import Paginator
5+
from . import forms
6+
from taggit.models import Tag
7+
8+
9+
def post_list(request, tag_slug=None):
10+
if tag_slug:
11+
tag = get_object_or_404(Tag, slug=tag_slug)
12+
posts = models.Post.objects.filter(status='published',
13+
tags__in=[tag])
14+
else:
15+
posts = models.Post.objects.filter(status='published')
16+
paginator = Paginator(posts, 3)
17+
page_number = request.GET.get('page')
18+
# print(page_number)
19+
page_obj = paginator.get_page(page_number)
20+
21+
context = {
22+
'page_obj': page_obj
23+
}
24+
# print(posts)
25+
return render(request, "post_list.html", context=context)
26+
27+
28+
def post_detail(request, year, month, day, slug):
29+
post = get_object_or_404(models.Post,
30+
created_date__year=year,
31+
created_date__month=month,
32+
created_date__day=day,
33+
slug=slug,
34+
status='published')
35+
# print(post.status)
36+
comment_form = forms.CommentForm()
37+
new_comment = None
38+
39+
if request.method == 'POST':
40+
comment_form = forms.CommentForm(data=request.POST)
41+
if comment_form.is_valid():
42+
new_comment = comment_form.save(commit=False)
43+
new_comment.post = post
44+
new_comment.save()
45+
46+
context = {
47+
'post': post,
48+
'comment_form': comment_form,
49+
'new_comment': new_comment,
50+
}
51+
52+
return render(request, "post_detail.html", context=context)
53+
54+
55+
def contact_us(request):
56+
return render(request, "contact_us.html")

‎manage.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
import os
4+
import sys
5+
6+
7+
def main():
8+
"""Run administrative tasks."""
9+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
10+
try:
11+
from django.core.management import execute_from_command_line
12+
except ImportError as exc:
13+
raise ImportError(
14+
"Couldn't import Django. Are you sure it's installed and "
15+
"available on your PYTHONPATH environment variable? Did you "
16+
"forget to activate a virtual environment?"
17+
) from exc
18+
execute_from_command_line(sys.argv)
19+
20+
21+
if __name__ == '__main__':
22+
main()

‎mysite/__init__.py

Whitespace-only changes.

‎mysite/asgi.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
ASGI config for mysite project.
3+
4+
It exposes the ASGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.asgi import get_asgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
15+
16+
application = get_asgi_application()

‎mysite/settings.py

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""
2+
Django settings for mysite project.
3+
4+
Generated by 'django-admin startproject' using Django 3.1.7.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/3.1/topics/settings/
8+
9+
For the full list of settings and their values, see
10+
https://docs.djangoproject.com/en/3.1/ref/settings/
11+
"""
12+
13+
from pathlib import Path
14+
15+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
16+
BASE_DIR = Path(__file__).resolve().parent.parent
17+
18+
19+
# Quick-start development settings - unsuitable for production
20+
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
21+
22+
# SECURITY WARNING: keep the secret key used in production secret!
23+
SECRET_KEY = '9!6d)ri=)4hfh5xmp_o=l40(p$mhyoyrf!f^cpkg-@n5$&+fa*'
24+
25+
# SECURITY WARNING: don't run with debug turned on in production!
26+
DEBUG = True
27+
28+
ALLOWED_HOSTS = []
29+
30+
31+
# Application definition
32+
33+
INSTALLED_APPS = [
34+
'django.contrib.admin',
35+
'django.contrib.auth',
36+
'django.contrib.contenttypes',
37+
'django.contrib.sessions',
38+
'django.contrib.messages',
39+
'django.contrib.staticfiles',
40+
'blog',
41+
'taggit',
42+
]
43+
44+
MIDDLEWARE = [
45+
'django.middleware.security.SecurityMiddleware',
46+
'django.contrib.sessions.middleware.SessionMiddleware',
47+
'django.middleware.common.CommonMiddleware',
48+
'django.middleware.csrf.CsrfViewMiddleware',
49+
'django.contrib.auth.middleware.AuthenticationMiddleware',
50+
'django.contrib.messages.middleware.MessageMiddleware',
51+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
52+
]
53+
54+
ROOT_URLCONF = 'mysite.urls'
55+
56+
TEMPLATES = [
57+
{
58+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
59+
'DIRS': [],
60+
'APP_DIRS': True,
61+
'OPTIONS': {
62+
'context_processors': [
63+
'django.template.context_processors.debug',
64+
'django.template.context_processors.request',
65+
'django.contrib.auth.context_processors.auth',
66+
'django.contrib.messages.context_processors.messages',
67+
],
68+
},
69+
},
70+
]
71+
72+
WSGI_APPLICATION = 'mysite.wsgi.application'
73+
74+
75+
# Database
76+
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
77+
78+
DATABASES = {
79+
'default': {
80+
'ENGINE': 'django.db.backends.sqlite3',
81+
'NAME': BASE_DIR / 'db.sqlite3',
82+
}
83+
}
84+
85+
86+
# Password validation
87+
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
88+
89+
AUTH_PASSWORD_VALIDATORS = [
90+
{
91+
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
92+
},
93+
{
94+
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
95+
},
96+
{
97+
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
98+
},
99+
{
100+
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
101+
},
102+
]
103+
104+
105+
# Internationalization
106+
# https://docs.djangoproject.com/en/3.1/topics/i18n/
107+
108+
LANGUAGE_CODE = 'en-us'
109+
110+
TIME_ZONE = 'UTC'
111+
112+
USE_I18N = True
113+
114+
USE_L10N = True
115+
116+
USE_TZ = True
117+
118+
119+
# Static files (CSS, JavaScript, Images)
120+
# https://docs.djangoproject.com/en/3.1/howto/static-files/
121+
122+
STATIC_URL = '/static/'

‎mysite/urls.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.contrib import admin
2+
from django.urls import path, include
3+
4+
urlpatterns = [
5+
path('admin/', admin.site.urls),
6+
path('', include('blog.urls')),
7+
]

‎mysite/wsgi.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
WSGI config for mysite project.
3+
4+
It exposes the WSGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.wsgi import get_wsgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
15+
16+
application = get_wsgi_application()

‎requirements.txt

174 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.