Compare commits
4 commits
54e0c2b793
...
612633c9d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
612633c9d8 | ||
|
|
9c7dd935da | ||
|
|
fc35b28704 | ||
|
|
cbc6ad4e70 |
8 changed files with 92 additions and 28 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -21,4 +21,4 @@ static/
|
||||||
staticfiles/
|
staticfiles/
|
||||||
|
|
||||||
# Ignore logs
|
# Ignore logs
|
||||||
*.log
|
*.log
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,33 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Plant
|
from .models import Plant, PlantImage
|
||||||
|
|
||||||
|
# Inline class for handling images
|
||||||
|
class PlantImageInline(admin.TabularInline):
|
||||||
|
model = PlantImage
|
||||||
|
extra = 3 # Display three empty forms for adding new images
|
||||||
|
fields = ('image',) # Only include fields that exist on PlantImage
|
||||||
|
|
||||||
|
can_delete = True # Allow deletion of images
|
||||||
|
max_num = 10 # Limit to 10 images per plant (optional)
|
||||||
|
|
||||||
@admin.register(Plant)
|
@admin.register(Plant)
|
||||||
class PlantAdmin(admin.ModelAdmin):
|
class PlantAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'image')
|
list_display = ('id', 'name', 'description', 'get_images', 'buy_price', 'sell_price', 'stock')
|
||||||
search_fields = ('name',)
|
search_fields = ('name','description') # Add search functionality
|
||||||
|
inlines = [PlantImageInline] # Add inline editing for images
|
||||||
|
|
||||||
|
def save_related(self, request, form, formsets, change):
|
||||||
|
super().save_related(request, form, formsets, change)
|
||||||
|
# Ensure all related PlantImage objects are saved correctly
|
||||||
|
for formset in formsets:
|
||||||
|
if hasattr(formset, 'save'):
|
||||||
|
formset.save()
|
||||||
|
|
||||||
|
def get_images(self, obj):
|
||||||
|
"""Display the first image or a placeholder in the list view."""
|
||||||
|
images = obj.images.all() # Access related images via the 'images' related_name
|
||||||
|
if images.exists():
|
||||||
|
return images[0].image.url # Show URL of the first image
|
||||||
|
return "No Images"
|
||||||
|
|
||||||
|
get_images.short_description = "Images" # Header for the admin column
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,14 @@
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||||
from rest_framework.filters import SearchFilter
|
from rest_framework.filters import SearchFilter
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from .permissions import IsAuthenticatedOrReadOnly
|
from .permissions import IsAuthenticatedOrReadOnly
|
||||||
from .models import Plant
|
from .models import Plant, PlantImage
|
||||||
from .serializers import PlantSerializer
|
from .serializers import PlantSerializer
|
||||||
|
|
||||||
# List and Create Plants
|
# List and Create Plants
|
||||||
|
|
@ -22,4 +27,14 @@ class PlantRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
queryset = Plant.objects.all()
|
queryset = Plant.objects.all()
|
||||||
serializer_class = PlantSerializer
|
serializer_class = PlantSerializer
|
||||||
parser_classes = [JSONParser, MultiPartParser, FormParser] # Allow file uploads and form data
|
parser_classes = [JSONParser, MultiPartParser, FormParser] # Allow file uploads and form data
|
||||||
permission_classes = [IsAuthenticatedOrReadOnly] # Apply custom permission
|
permission_classes = [IsAuthenticatedOrReadOnly] # Apply custom permission
|
||||||
|
|
||||||
|
|
||||||
|
class PlantImageDeleteAPIView(APIView):
|
||||||
|
permission_classes = [IsAuthenticatedOrReadOnly] # Ensure only authenticated users can delete
|
||||||
|
|
||||||
|
def delete(self, request, image_id):
|
||||||
|
image = get_object_or_404(PlantImage, id=image_id)
|
||||||
|
image.delete() # Delete the image object from the database
|
||||||
|
return Response({"message": "Image deleted successfully"}, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
# catalog/models.py
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Plant(models.Model):
|
class Plant(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
description = models.TextField(null=True, blank=True) # Allow null and blank
|
description = models.TextField(null=True, blank=True)
|
||||||
image = models.ImageField(upload_to='plants/', null=True, blank=True) # Optional image
|
buy_price = models.FloatField(null=True, blank=True)
|
||||||
buy_price = models.FloatField(null=True, blank=True) # Allow null for buy price
|
sell_price = models.FloatField(null=True, blank=True)
|
||||||
sell_price = models.FloatField(null=True, blank=True) # Allow null for sell price
|
stock = models.IntegerField(default=0)
|
||||||
stock = models.IntegerField(default=0) # Default to 0
|
|
||||||
|
|
||||||
def __str__(self):
|
class PlantImage(models.Model):
|
||||||
return self.name
|
plant = models.ForeignKey(Plant, related_name='images', on_delete=models.CASCADE)
|
||||||
|
image = models.ImageField(upload_to='plants/')
|
||||||
|
|
|
||||||
0
plant_catalog/catalog/permissions.py
Normal file → Executable file
0
plant_catalog/catalog/permissions.py
Normal file → Executable file
|
|
@ -1,17 +1,41 @@
|
||||||
# catalog/serializers.py
|
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Plant
|
from .models import Plant, PlantImage
|
||||||
|
|
||||||
|
class PlantImageSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = PlantImage
|
||||||
|
fields = ['id', 'image'] # Fields to expose for each image
|
||||||
|
|
||||||
|
|
||||||
class PlantSerializer(serializers.ModelSerializer):
|
class PlantSerializer(serializers.ModelSerializer):
|
||||||
|
images = PlantImageSerializer(many=True, read_only=True) # Read existing images
|
||||||
|
uploaded_images = serializers.ListField(child=serializers.ImageField(), write_only=True, required=False) # Accept new images
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Plant
|
model = Plant
|
||||||
fields = ['id', 'name', 'description', 'image', 'buy_price', 'sell_price', 'stock'] # Include all fields
|
fields = ['id', 'name', 'description', 'buy_price', 'sell_price', 'stock', 'images', 'uploaded_images'] # Include uploaded_images
|
||||||
extra_kwargs = {
|
|
||||||
'buy_price': {'required': False, 'allow_null': True}, # Allow null for buy price
|
def create(self, validated_data):
|
||||||
'sell_price': {'required': False, 'allow_null': True}, # Allow null for sell price
|
uploaded_images = validated_data.pop('uploaded_images', []) # Get images from request
|
||||||
'image': {'required': False, 'allow_null': True}, # Allow image to be optional
|
plant = Plant.objects.create(**validated_data)
|
||||||
'description': {'required': False, 'allow_null': True}, # Optional description
|
|
||||||
}
|
for image in uploaded_images:
|
||||||
|
PlantImage.objects.create(plant=plant, image=image) # Save each image
|
||||||
|
|
||||||
|
return plant
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
uploaded_images = validated_data.pop('uploaded_images', [])
|
||||||
|
|
||||||
|
instance.name = validated_data.get('name', instance.name)
|
||||||
|
instance.description = validated_data.get('description', instance.description)
|
||||||
|
instance.buy_price = validated_data.get('buy_price', instance.buy_price)
|
||||||
|
instance.sell_price = validated_data.get('sell_price', instance.sell_price)
|
||||||
|
instance.stock = validated_data.get('stock', instance.stock)
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
for image in uploaded_images:
|
||||||
|
PlantImage.objects.create(plant=instance, image=image) # Save new images
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
||||||
from .api_views import PlantListCreateAPIView, PlantRetrieveUpdateDestroyAPIView
|
from .api_views import PlantListCreateAPIView, PlantRetrieveUpdateDestroyAPIView, PlantImageDeleteAPIView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', lambda request: HttpResponseRedirect('/api/plants/')),
|
path('', lambda request: HttpResponseRedirect('/api/plants/')),
|
||||||
|
|
@ -11,6 +11,7 @@ urlpatterns = [
|
||||||
path('api/plants/<int:pk>/', PlantRetrieveUpdateDestroyAPIView.as_view(), name='plant_detail_update_delete'), # Retrieve, Update, Delete
|
path('api/plants/<int:pk>/', PlantRetrieveUpdateDestroyAPIView.as_view(), name='plant_detail_update_delete'), # Retrieve, Update, Delete
|
||||||
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||||
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||||
|
path('api/plant-images/<int:image_id>/', PlantImageDeleteAPIView.as_view(), name='plant_image_delete'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-(e9sx(r=75hv%xni#rcm4)0&67c34+her^!%7dqlghem1=l0lc
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['gonzalohd.eu', 'plantsapi.gonzalohd.eu', 'localhost', '192.168.1.139', '127.0.0.1']
|
ALLOWED_HOSTS = ['gonzalohd.com', 'plantsapi.gonzalohd.com', 'localhost', '192.168.1.139', '127.0.0.1']
|
||||||
# ALLOWED_HOSTS = ['*']
|
# ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue