Skip to content

Commit 219bb72

Browse files
committed
Added basic image upload, added view for images
- Added Image model - Added graphql query for images - Added image upload api on url upload/ - Added image serving on url images/*.*
1 parent e0b0149 commit 219bb72

File tree

8 files changed

+92
-13
lines changed

8 files changed

+92
-13
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
.direnv/
44
venv/
55
blog/migrations/*
6+
media/

backend/settings.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
"""
1212

1313
from pathlib import Path
14+
import os
1415

1516
# Build paths inside the project like this: BASE_DIR / 'subdir'.
1617
BASE_DIR = Path(__file__).resolve().parent.parent
17-
18+
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
19+
MEDIA_URL = "/"
1820

1921
# Quick-start development settings - unsuitable for production
2022
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
@@ -25,7 +27,7 @@
2527
# SECURITY WARNING: don't run with debug turned on in production!
2628
DEBUG = True
2729

28-
ALLOWED_HOSTS = []
30+
ALLOWED_HOSTS = ["localhost"]
2931

3032

3133
# Application definition

backend/urls.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,24 @@
1717
from django.contrib import admin
1818
from django.urls import path
1919

20+
from django.conf import settings
21+
from django.conf.urls.static import static
22+
2023
from django.views.decorators.csrf import csrf_exempt
2124
from graphene_django.views import GraphQLView
2225

2326
from blog.feeds import RssTutorialsFeeds
24-
from blog.views import TutorialListView, TutorialDetailView
27+
from blog.views import TutorialListView, TutorialDetailView, upload_image, image_view
2528

2629
urlpatterns = [
2730
path("admin", admin.site.urls),
2831
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
2932
path("feed", RssTutorialsFeeds(), name="tutorial_feed"),
3033
path("<slug:slug>", TutorialDetailView.as_view(), name="tutorial_detail"),
3134
path("", TutorialListView.as_view(), name="tutorial_list"),
35+
path("upload/", upload_image, name="upload_image"),
36+
path("images/<str:image_name>", image_view, name="image_view"),
3237
]
38+
39+
if settings.DEBUG:
40+
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

blog/admin.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.contrib import admin
22

3-
from blog.models import Profile, Post, Tag
3+
from blog.models import Profile, Post, Tag, Image
44

55

66
@admin.register(Profile)
@@ -50,3 +50,10 @@ class PostAdmin(admin.ModelAdmin):
5050
}
5151
date_hierarchy = "publish_date"
5252
save_on_top = True
53+
54+
55+
@admin.register(Image)
56+
class ImageAdmin(admin.ModelAdmin):
57+
model = Image
58+
59+
list_display = ("id", "name", "Main_Img")

blog/models.py

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ def __str__(self):
2222
return self.name
2323

2424

25+
class Image(models.Model):
26+
name = models.CharField(max_length=50)
27+
alt_text = models.CharField(max_length=100)
28+
Main_Img = models.ImageField(upload_to="images/")
29+
30+
def __str__(self):
31+
return self.name
32+
33+
2534
class Post(models.Model):
2635
class Meta:
2736
ordering = ["-publish_date"]

blog/schema.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@ class Meta:
2525
model = models.Tag
2626

2727

28+
class ImageType(DjangoObjectType):
29+
class Meta:
30+
model = models.Image
31+
32+
2833
class Query(graphene.ObjectType):
2934
all_posts = graphene.List(PostType)
3035
all_tags = graphene.List(TagType)
3136
all_authors = graphene.List(AuthorType)
37+
all_images = graphene.List(ImageType)
3238
latest_posts = graphene.List(PostType)
3339
author_by_username = graphene.Field(AuthorType, username=graphene.String())
3440
post_by_slug = graphene.Field(PostType, slug=graphene.String())
@@ -37,7 +43,9 @@ class Query(graphene.ObjectType):
3743
posts_by_id = graphene.List(PostType, id=graphene.ID())
3844

3945
def resolve_all_posts(root, info, order_by=None):
40-
return models.Post.objects.prefetch_related("tags").select_related("author").all()
46+
return (
47+
models.Post.objects.prefetch_related("tags").select_related("author").all()
48+
)
4149

4250
def resolve_all_tags(root, info):
4351
return models.Tag.objects.all()
@@ -75,7 +83,14 @@ def resolve_posts_by_id(root, info, id):
7583
return models.Post.objects.filter(id=id)
7684

7785
def resolve_latest_posts(root, info):
78-
return models.Post.objects.prefetch_related("tags").select_related("author").order_by("-id")[:5]
86+
return (
87+
models.Post.objects.prefetch_related("tags")
88+
.select_related("author")
89+
.order_by("-id")[:5]
90+
)
91+
92+
def resolve_all_images(root, info):
93+
return models.Image.objects.all()
7994

8095

8196
schema = graphene.Schema(query=Query)

blog/views.py

+43-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,51 @@
1-
from django.shortcuts import render
1+
# from django.shortcuts import render
22
from django.views.generic import ListView, DetailView
3+
from django.http import JsonResponse, HttpResponse
4+
from django.views.decorators.csrf import csrf_exempt
5+
6+
7+
from .models import Post, Image
8+
39

4-
from .models import Post
510
# Create your views here.
611
class TutorialListView(ListView):
712
model = Post
8-
context_object_name = 'tutorial_list'
9-
template_name = 'tutorials/tutorial_list.html'
13+
context_object_name = "tutorial_list"
14+
template_name = "tutorials/tutorial_list.html"
1015

1116

12-
class TutorialDetailView(DetailView):
17+
class TutorialDetailView(DetailView):
1318
model = Post
14-
context_object_name = 'tutorial'
15-
template_name = 'tutorials/tutorial_detail.html'
19+
context_object_name = "tutorial"
20+
template_name = "tutorials/tutorial_detail.html"
21+
22+
23+
# @csrf_exempt # Note: Only for simplicity, use proper CSRF protection in production
24+
def upload_image(request):
25+
if request.method == "POST":
26+
# image_name = request.POST.get("image_name")
27+
image_file = request.FILES.get("image_file")
28+
alt_text = request.POST.get("alt_text")
29+
30+
if not image_file or not alt_text:
31+
return JsonResponse(
32+
{"error": "Please provide all required fields"}, status=400
33+
)
34+
35+
# Create a new Image instance
36+
new_image = Image(name=image_file.name, alt_text=alt_text, Main_Img=image_file)
37+
new_image.save()
38+
39+
return JsonResponse({"message": "Image uploaded successfully"})
40+
else:
41+
return JsonResponse({"error": "Only POST requests are allowed"}, status=405)
42+
43+
44+
def image_view(request, image_name):
45+
try:
46+
image = Image.objects.get(name=image_name)
47+
with open(image.Main_Img.path, "rb") as img_file:
48+
response = HttpResponse(img_file.read(), content_type="image/jpeg")
49+
return response
50+
except Image.DoesNotExist:
51+
return HttpResponse("Image not found", status=404)

flake.nix

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
python311Packages.djangorestframework
1818
python311Packages.graphene-django
1919
python311Packages.django-cors-headers
20+
python311Packages.pillow
2021
];
2122
};
2223
};

0 commit comments

Comments
 (0)