Add image upload support
Images can be uploaded, thumbnails are created, they're displayed within the threads themselves. Just like four chans! There is not an upload size limit set yet. Gotta get on that next. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
1
Pipfile
1
Pipfile
@@ -5,6 +5,7 @@ name = "pypi"
|
|||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
django = "*"
|
django = "*"
|
||||||
|
pillow = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
mypy = "*"
|
mypy = "*"
|
||||||
|
|||||||
96
Pipfile.lock
generated
96
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "5b45ddb3eaf4d3c3557de0cab9ab514fcebdfe6e6c80120a11f223011f9beec6"
|
"sha256": "720e8538e0b5a6418b2f8e2973ef59da19390569ba586790a194f490bdabd90b"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"asgiref": {
|
"asgiref": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0",
|
"sha256:45a429524fba18aba9d512498b19d220c4d628e75b40cf5c627524dbaebc5cc1",
|
||||||
"sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"
|
"sha256:fddeea3c53fa99d0cdb613c3941cc6e52d822491fc2753fba25768fb5bf4e865"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==3.5.0"
|
"version": "==3.5.1"
|
||||||
},
|
},
|
||||||
"django": {
|
"django": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -32,41 +32,49 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.0.4"
|
"version": "==4.0.4"
|
||||||
},
|
},
|
||||||
"mypy": {
|
"pillow": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0112752a6ff07230f9ec2f71b0d3d4e088a910fdce454fdb6553e83ed0eced7d",
|
"sha256:01ce45deec9df310cbbee11104bae1a2a43308dd9c317f99235b6d3080ddd66e",
|
||||||
"sha256:0384d9f3af49837baa92f559d3fa673e6d2652a16550a9ee07fc08c736f5e6f8",
|
"sha256:0c51cb9edac8a5abd069fd0758ac0a8bfe52c261ee0e330f363548aca6893595",
|
||||||
"sha256:1b333cfbca1762ff15808a0ef4f71b5d3eed8528b23ea1c3fb50543c867d68de",
|
"sha256:17869489de2fce6c36690a0c721bd3db176194af5f39249c1ac56d0bb0fcc512",
|
||||||
"sha256:1fdeb0a0f64f2a874a4c1f5271f06e40e1e9779bf55f9567f149466fc7a55038",
|
"sha256:21dee8466b42912335151d24c1665fcf44dc2ee47e021d233a40c3ca5adae59c",
|
||||||
"sha256:4c653e4846f287051599ed8f4b3c044b80e540e88feec76b11044ddc5612ffed",
|
"sha256:25023a6209a4d7c42154073144608c9a71d3512b648a2f5d4465182cb93d3477",
|
||||||
"sha256:563514c7dc504698fb66bb1cf897657a173a496406f1866afae73ab5b3cdb334",
|
"sha256:255c9d69754a4c90b0ee484967fc8818c7ff8311c6dddcc43a4340e10cd1636a",
|
||||||
"sha256:5b231afd6a6e951381b9ef09a1223b1feabe13625388db48a8690f8daa9b71ff",
|
"sha256:35be4a9f65441d9982240e6966c1eaa1c654c4e5e931eaf580130409e31804d4",
|
||||||
"sha256:5ce6a09042b6da16d773d2110e44f169683d8cc8687e79ec6d1181a72cb028d2",
|
"sha256:3f42364485bfdab19c1373b5cd62f7c5ab7cc052e19644862ec8f15bb8af289e",
|
||||||
"sha256:5e7647df0f8fc947388e6251d728189cfadb3b1e558407f93254e35abc026e22",
|
"sha256:3fddcdb619ba04491e8f771636583a7cc5a5051cd193ff1aa1ee8616d2a692c5",
|
||||||
"sha256:6003de687c13196e8a1243a5e4bcce617d79b88f83ee6625437e335d89dfebe2",
|
"sha256:463acf531f5d0925ca55904fa668bb3461c3ef6bc779e1d6d8a488092bdee378",
|
||||||
"sha256:61504b9a5ae166ba5ecfed9e93357fd51aa693d3d434b582a925338a2ff57fd2",
|
"sha256:4fe29a070de394e449fd88ebe1624d1e2d7ddeed4c12e0b31624561b58948d9a",
|
||||||
"sha256:77423570c04aca807508a492037abbd72b12a1fb25a385847d191cd50b2c9605",
|
"sha256:55dd1cf09a1fd7c7b78425967aacae9b0d70125f7d3ab973fadc7b5abc3de652",
|
||||||
"sha256:a4d9898f46446bfb6405383b57b96737dcfd0a7f25b748e78ef3e8c576bba3cb",
|
"sha256:5a3ecc026ea0e14d0ad7cd990ea7f48bfcb3eb4271034657dc9d06933c6629a7",
|
||||||
"sha256:a952b8bc0ae278fc6316e6384f67bb9a396eb30aced6ad034d3a76120ebcc519",
|
"sha256:5cfca31ab4c13552a0f354c87fbd7f162a4fafd25e6b521bba93a57fe6a3700a",
|
||||||
"sha256:b5b5bd0ffb11b4aba2bb6d31b8643902c48f990cc92fda4e21afac658044f0c0",
|
"sha256:66822d01e82506a19407d1afc104c3fcea3b81d5eb11485e593ad6b8492f995a",
|
||||||
"sha256:ca75ecf2783395ca3016a5e455cb322ba26b6d33b4b413fcdedfc632e67941dc",
|
"sha256:69e5ddc609230d4408277af135c5b5c8fe7a54b2bdb8ad7c5100b86b3aab04c6",
|
||||||
"sha256:cf9c261958a769a3bd38c3e133801ebcd284ffb734ea12d01457cb09eacf7d7b",
|
"sha256:6b6d4050b208c8ff886fd3db6690bf04f9a48749d78b41b7a5bf24c236ab0165",
|
||||||
"sha256:dd4d670eee9610bf61c25c940e9ade2d0ed05eb44227275cce88701fee014b1f",
|
"sha256:7a053bd4d65a3294b153bdd7724dce864a1d548416a5ef61f6d03bf149205160",
|
||||||
"sha256:e19736af56947addedce4674c0971e5dceef1b5ec7d667fe86bcd2b07f8f9075",
|
"sha256:82283af99c1c3a5ba1da44c67296d5aad19f11c535b551a5ae55328a317ce331",
|
||||||
"sha256:eaea21d150fb26d7b4856766e7addcf929119dd19fc832b22e71d942835201ef",
|
"sha256:8782189c796eff29dbb37dd87afa4ad4d40fc90b2742704f94812851b725964b",
|
||||||
"sha256:eaff8156016487c1af5ffa5304c3e3fd183edcb412f3e9c72db349faf3f6e0eb",
|
"sha256:8d79c6f468215d1a8415aa53d9868a6b40c4682165b8cb62a221b1baa47db458",
|
||||||
"sha256:ee0a36edd332ed2c5208565ae6e3a7afc0eabb53f5327e281f2ef03a6bc7687a",
|
"sha256:97bda660702a856c2c9e12ec26fc6d187631ddfd896ff685814ab21ef0597033",
|
||||||
"sha256:ef7beb2a3582eb7a9f37beaf38a28acfd801988cde688760aea9e6cc4832b10b"
|
"sha256:a325ac71914c5c043fa50441b36606e64a10cd262de12f7a179620f579752ff8",
|
||||||
|
"sha256:a336a4f74baf67e26f3acc4d61c913e378e931817cd1e2ef4dfb79d3e051b481",
|
||||||
|
"sha256:a598d8830f6ef5501002ae85c7dbfcd9c27cc4efc02a1989369303ba85573e58",
|
||||||
|
"sha256:a5eaf3b42df2bcda61c53a742ee2c6e63f777d0e085bbc6b2ab7ed57deb13db7",
|
||||||
|
"sha256:aea7ce61328e15943d7b9eaca87e81f7c62ff90f669116f857262e9da4057ba3",
|
||||||
|
"sha256:af79d3fde1fc2e33561166d62e3b63f0cc3e47b5a3a2e5fea40d4917754734ea",
|
||||||
|
"sha256:c24f718f9dd73bb2b31a6201e6db5ea4a61fdd1d1c200f43ee585fc6dcd21b34",
|
||||||
|
"sha256:c5b0ff59785d93b3437c3703e3c64c178aabada51dea2a7f2c5eccf1bcf565a3",
|
||||||
|
"sha256:c7110ec1701b0bf8df569a7592a196c9d07c764a0a74f65471ea56816f10e2c8",
|
||||||
|
"sha256:c870193cce4b76713a2b29be5d8327c8ccbe0d4a49bc22968aa1e680930f5581",
|
||||||
|
"sha256:c9efef876c21788366ea1f50ecb39d5d6f65febe25ad1d4c0b8dff98843ac244",
|
||||||
|
"sha256:de344bcf6e2463bb25179d74d6e7989e375f906bcec8cb86edb8b12acbc7dfef",
|
||||||
|
"sha256:eb1b89b11256b5b6cad5e7593f9061ac4624f7651f7a8eb4dfa37caa1dfaa4d0",
|
||||||
|
"sha256:ed742214068efa95e9844c2d9129e209ed63f61baa4d54dbf4cf8b5e2d30ccf2",
|
||||||
|
"sha256:f401ed2bbb155e1ade150ccc63db1a4f6c1909d3d378f7d1235a44e90d75fb97",
|
||||||
|
"sha256:fb89397013cf302f282f0fc998bb7abf11d49dcff72c8ecb320f76ea6e2c5717"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.950"
|
"version": "==9.1.0"
|
||||||
},
|
|
||||||
"mypy-extensions": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
|
||||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
|
||||||
],
|
|
||||||
"version": "==0.4.3"
|
|
||||||
},
|
},
|
||||||
"sqlparse": {
|
"sqlparse": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -75,22 +83,6 @@
|
|||||||
],
|
],
|
||||||
"markers": "python_version >= '3.5'",
|
"markers": "python_version >= '3.5'",
|
||||||
"version": "==0.4.2"
|
"version": "==0.4.2"
|
||||||
},
|
|
||||||
"tomli": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
|
||||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
|
||||||
],
|
|
||||||
"markers": "python_version < '3.11'",
|
|
||||||
"version": "==2.0.1"
|
|
||||||
},
|
|
||||||
"typing-extensions": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708",
|
|
||||||
"sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '3.7'",
|
|
||||||
"version": "==4.2.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {
|
"develop": {
|
||||||
|
|||||||
@@ -1,8 +1,42 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_save
|
from django.db.models import signals
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from PIL import Image
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
|
def image_upload(instance, filename):
|
||||||
|
op_id = instance.op.id if instance.op else instance.id
|
||||||
|
now = timezone.now()
|
||||||
|
now_sec = now.strftime("%s.%f")
|
||||||
|
ext = Path(filename).suffix.lower()
|
||||||
|
if ext in (".jpeg", ".jpg"):
|
||||||
|
ext = ".jpg"
|
||||||
|
|
||||||
|
if ext not in (".jpg", ".png", ".gif"):
|
||||||
|
raise Exception("File type invalid")
|
||||||
|
|
||||||
|
if instance.op:
|
||||||
|
return f"{instance.board.url}/{instance.op.id}/{now_sec}{ext}"
|
||||||
|
else:
|
||||||
|
return f"{instance.board.url}/{now_sec}{ext}"
|
||||||
|
|
||||||
|
|
||||||
|
def thumbs_upload(instance, filename):
|
||||||
|
op_id = instance.op.id if instance.op else instance.id
|
||||||
|
now = timezone.now()
|
||||||
|
now_sec = now.strftime("%s.%f")
|
||||||
|
ext = Path(filename).suffix.lower()
|
||||||
|
if instance.op:
|
||||||
|
return f"{instance.board.url}/{instance.op.id}/{now_sec}{ext}"
|
||||||
|
else:
|
||||||
|
return f"{instance.board.url}/{now_sec}t{ext}"
|
||||||
|
|
||||||
|
|
||||||
class Board(models.Model):
|
class Board(models.Model):
|
||||||
@@ -39,8 +73,52 @@ class Post(models.Model):
|
|||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
# Last bump time
|
# Last bump time
|
||||||
last_bump = models.DateTimeField(auto_now_add=True)
|
last_bump = models.DateTimeField(auto_now_add=True)
|
||||||
|
# Image
|
||||||
|
image = models.ImageField(
|
||||||
|
upload_to=image_upload,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
width_field="image_width",
|
||||||
|
height_field="image_height",
|
||||||
|
)
|
||||||
|
# Thumbnail
|
||||||
|
thumbnail = models.ImageField(upload_to=thumbs_upload, editable=False, null=True)
|
||||||
|
# Original image name
|
||||||
|
original_image_name = models.CharField(max_length=255, null=True)
|
||||||
|
# Image width and height
|
||||||
|
image_width = models.IntegerField(null=True)
|
||||||
|
image_height = models.IntegerField(null=True)
|
||||||
|
|
||||||
# TODO : images
|
def save(self, *args, **kwargs):
|
||||||
|
if self.image:
|
||||||
|
self.original_image_name = self.image.name
|
||||||
|
self.__make_thumbnail()
|
||||||
|
super(Post, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def __make_thumbnail(self):
|
||||||
|
image = Image.open(self.image)
|
||||||
|
image.thumbnail(settings.THUMB_SIZE, Image.ANTIALIAS)
|
||||||
|
|
||||||
|
image_path = Path(self.image.name)
|
||||||
|
thumb_path = Path(image_path.stem + "t" + image_path.suffix)
|
||||||
|
|
||||||
|
ext = thumb_path.suffix.lower()
|
||||||
|
if ext in (".jpg", ".jpeg"):
|
||||||
|
FTYPE = "JPEG"
|
||||||
|
elif ext == ".gif":
|
||||||
|
FTYPE = "GIF"
|
||||||
|
elif ext == ".png":
|
||||||
|
FTYPE = "PNG"
|
||||||
|
else:
|
||||||
|
raise Exception("File type invalid")
|
||||||
|
|
||||||
|
thumb_temp = BytesIO()
|
||||||
|
image.save(thumb_temp, FTYPE)
|
||||||
|
thumb_temp.seek(0)
|
||||||
|
|
||||||
|
# save=False stops recursion
|
||||||
|
self.thumbnail.save(thumb_path, ContentFile(thumb_temp.read()), save=False)
|
||||||
|
thumb_temp.close()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
if self.op is None:
|
if self.op is None:
|
||||||
@@ -57,8 +135,16 @@ class Post(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Post)
|
@receiver(signals.post_save, sender=Post)
|
||||||
def post_created(sender, instance, created, **kwargs):
|
def post_created(sender, instance, created, **kwargs):
|
||||||
if created and instance.op:
|
if created and instance.op:
|
||||||
instance.op.last_bump = timezone.now()
|
instance.op.last_bump = timezone.now()
|
||||||
instance.op.save()
|
instance.op.save()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(signals.post_delete, sender=Post)
|
||||||
|
def post_deleted(sender, instance, **kwargs):
|
||||||
|
if instance.image and Path(instance.image.path).is_file():
|
||||||
|
os.remove(instance.image.path)
|
||||||
|
if instance.thumbnail and Path(instance.thumbnail.path).is_file():
|
||||||
|
os.remove(instance.thumbnail.path)
|
||||||
|
|||||||
@@ -16,6 +16,24 @@ hr {
|
|||||||
|
|
||||||
/* Posts */
|
/* Posts */
|
||||||
/*.post_body { }*/
|
/*.post_body { }*/
|
||||||
|
.post_image_info {
|
||||||
|
font-size: small;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_image_thumbnail {
|
||||||
|
float: left;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Not sure if I like the in-line style or each post gets its own row style
|
||||||
|
.post_content:after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
.post_id {
|
.post_id {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<h2>Create a new thread</h2>
|
<h2>Create a new thread</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<form method="post" action="{% url 'board:board_detail' url=board.url %}">
|
<form method="post" action="{% url 'board:board_detail' url=board.url %}" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table>
|
<table>
|
||||||
{{ form.as_table }}
|
{{ form.as_table }}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="column"> </div>
|
<div class="column"> </div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<form method="post" action="{% url 'board:post_detail' url=board.url id=post.id %}">
|
<form method="post" action="{% url 'board:post_detail' url=board.url id=post.id %}" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table>
|
<table>
|
||||||
{{ form.as_table }}
|
{{ form.as_table }}
|
||||||
|
|||||||
@@ -1,6 +1,23 @@
|
|||||||
{% load post_body %}
|
{% load post_body %}
|
||||||
<div id="p{{post.id}}">
|
<div id="p{{post.id}}">
|
||||||
{# TODO images #}
|
{# Image #}
|
||||||
|
{% if post.thumbnail %}
|
||||||
|
{# Image info #}
|
||||||
|
<div class="post_image_info">
|
||||||
|
File: <a href="{{post.image.url}}" target="_blank">{{post.original_image_name}}</a>
|
||||||
|
({{post.image.size|measure_bytes}}, {{post.image_width}}x{{post.image_height}})
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Image thumbnail #}
|
||||||
|
<div class="post_image_thumbnail">
|
||||||
|
<a href="{{post.image.url}}" target="_blank">
|
||||||
|
<img src="{{post.thumbnail.url}}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# Post ID, username, time #}
|
||||||
|
<div class="post_content">
|
||||||
<a href="#p{{post.id}}">#.</a>
|
<a href="#p{{post.id}}">#.</a>
|
||||||
<span class="post_id">{{post.id}}</span>
|
<span class="post_id">{{post.id}}</span>
|
||||||
{% if post.subject %}
|
{% if post.subject %}
|
||||||
@@ -11,11 +28,16 @@ at {{post.created}}
|
|||||||
{% if reply_link %}
|
{% if reply_link %}
|
||||||
[<a href="{{post.get_absolute_url}}">Reply</a>]
|
[<a href="{{post.get_absolute_url}}">Reply</a>]
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# "X replies elided" dialog for OPs on the board #}
|
||||||
{% if replies_elided > 0 %}
|
{% if replies_elided > 0 %}
|
||||||
<br/>
|
<br/>
|
||||||
<span class="replies_elided">
|
<span class="replies_elided">
|
||||||
({{replies_elided}} replies elided, click reply to view)
|
({{replies_elided}} replies elided, click reply to view)
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# Post body #}
|
||||||
<p class="post_body">{{post|post_body|safe}}</p>
|
<p class="post_body">{{post|post_body|safe}}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,3 +91,13 @@ def post_body(post: Post) -> str:
|
|||||||
# Finally, replace linebreaks
|
# Finally, replace linebreaks
|
||||||
text = text.replace("\n", "<br/>")
|
text = text.replace("\n", "<br/>")
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="measure_bytes")
|
||||||
|
def measure_bytes(value: int) -> str:
|
||||||
|
if value < 1024:
|
||||||
|
return f"{value} bytes"
|
||||||
|
elif value < 1024**2:
|
||||||
|
return f"{round(value / 1024, 1)} KiB"
|
||||||
|
else:
|
||||||
|
return f"{round(value / (1024**2), 1)} MiB"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls.static import static
|
||||||
from board.views import *
|
from board.views import *
|
||||||
|
|
||||||
|
|
||||||
@@ -8,3 +10,5 @@ urlpatterns = [
|
|||||||
path("<slug:url>/page/<int:page>/", BoardView.as_view(), name="board_detail"),
|
path("<slug:url>/page/<int:page>/", BoardView.as_view(), name="board_detail"),
|
||||||
path("<slug:url>/post/<int:id>/", PostView.as_view(), name="post_detail"),
|
path("<slug:url>/post/<int:id>/", PostView.as_view(), name="post_detail"),
|
||||||
]
|
]
|
||||||
|
# TODO - make this conditional so we can serve images up with whatever server we want
|
||||||
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def get_client_ip(request):
|
|||||||
|
|
||||||
class BoardView(CreateView):
|
class BoardView(CreateView):
|
||||||
model = Post
|
model = Post
|
||||||
fields = ["subject", "name", "text"]
|
fields = ["subject", "name", "text", "image"]
|
||||||
slug_field = "url"
|
slug_field = "url"
|
||||||
slug_url_kwarg = "url"
|
slug_url_kwarg = "url"
|
||||||
|
|
||||||
@@ -57,13 +57,12 @@ class BoardView(CreateView):
|
|||||||
form.instance.ip = get_client_ip(self.request)
|
form.instance.ip = get_client_ip(self.request)
|
||||||
|
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
class PostView(CreateView):
|
class PostView(CreateView):
|
||||||
model = Post
|
model = Post
|
||||||
fields = ["name", "text"]
|
fields = ["name", "text", "image"]
|
||||||
slug_field = "url"
|
slug_field = "url"
|
||||||
slug_url_kwarg = "url"
|
slug_url_kwarg = "url"
|
||||||
|
|
||||||
|
|||||||
@@ -124,3 +124,11 @@ STATIC_URL = "static/"
|
|||||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
# Media root - where media files are stored on the disk
|
||||||
|
MEDIA_ROOT = "media/"
|
||||||
|
|
||||||
|
# Media URL - the URL where media is served from
|
||||||
|
MEDIA_URL = "media/"
|
||||||
|
|
||||||
|
THUMB_SIZE = (200, 200)
|
||||||
|
|||||||
Reference in New Issue
Block a user