Django edit the admin template for models - django

I am new to Django and working on a project.
I have these models
class Test(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255, blank=True)
applicable_device = models.ManyToManyField(Device)
applicable_platform = models.ManyToManyField(Platform)
class Meta:
verbose_name = 'Test'
verbose_name_plural = 'Tests'
def __str__(self):
return self.name
class Event(models.Model):
name = models.CharField(max_length=255)
test = models.ManyToManyField(Test)
applicable_devices = models.ManyToManyField(Device)
class Meta:
verbose_name = 'Event'
verbose_name_plural = 'Events'
def __str__(self):
return self.name
class Property(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
applicable_events = models.ManyToManyField(Event)
applicable_devices = models.ManyToManyField(Device)
applicable_platform = models.ManyToManyField(Platform)
property_type = models.CharField(max_length=20, choices=TYPE_CHOICES)
expected_value = ArrayField(models.CharField(max_length=200), blank=True)
When I go to the Event section in the Django Admin Panel I am able to edit the events. But I want to be able to see a list of all the properties that apply to it underneath where I edit the event.
Is this possible?

I am not sure of what you have allready done in your admin .py. Did you modify your admin.py file and your form.py ?
form.py
from . import models
from django.forms.models import ModelForm
class EventForm(ModelForm):
class Meta:
model = models.Event
fields = ('id','name','test', 'applicable devices')
admin.py
from . import models
from . import forms
from django.contrib import admin
class EventAdmin(admin.ModelAdmin):
form = forms.EventForm
list_display = ['name','test', 'applicable devices']
# then to register your ModelAdmin
admin.site.register(Event, EventAdmin)
````

Related

how to save multiple objects to the database in django rest framework views

so what i'm trying to do is add a new product to my data base using django's restapi
but a product may contain multiple categories which are related throught a third many to many
model and extra pictures which are ForeignKeyed to the product
this is my models.py
class Products(models.Model):
product_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=35, null=False, unique=True)
description = models.CharField(max_length=255)
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.)
main_image = models.FileField(upload_to='shop/images')
created_on = models.DateTimeField(blank=True, default=datetime.now)
class Category(models.Model):
category_id = models.AutoField(primary_key=True)
category = models.CharField(max_length=20, null=True, blank=True)
created_on = models.DateTimeField(blank=True, default=datetime.now)
class Meta:
db_table = 'Category'
class ProductsCategory(models.Model):
productscategory_id = models.AutoField(primary_key=True)
category = models.ForeignKey(to=Category, on_delete=models.CASCADE)
product = models.ForeignKey(to=Products, on_delete=models.CASCADE)
created_on = models.DateTimeField(blank=True, default=datetime.now)
class Meta:
db_table = 'ProductsCategory'
class Pictures(models.Model):
picture_id = models.AutoField(primary_key=True)
image = models.FileField(upload_to='shop/images')
product = models.ForeignKey(to=Products, on_delete=models.CASCADE)
created_on = models.DateTimeField(blank=True, default=datetime.now)
class Meta:
db_table = 'Pictures'
and heres what i've tryed:
#api_view(['POST'])
#permission_classes([IsModerator])
def create_product(request):
product_details = ProductsSerializer(request.POST, request.FILES)
pictures = PicturesSerializer(request.POST, request.FILES, many=True)
category_list = request.POST.getlist("category")
if product_details.is_valid() and validate_file_extension(request.FILES.get("main_image")):
try:
product = product_details.save()
if len(category_list) > 0:
for i in category_list:
category = Category.objects.get(category=i)
ProductsCategory.objects.create(category=category, product=product)
if pictures:
for image in request.FILES.getlist("image"):
if validate_file_extension(image):
Pictures.objects.create(image=image, product=product)
else:
error = {"error": "invalid extra pictures extension"}
return Response(error)
return Response((product_details.data, pictures.data, category_list), status=status.HTTP_201_CREATED)
except Exception as e:
return Response(e)
else:
return Response((product_details._errors, pictures._errors), status=status.HTTP_400_BAD_REQUEST)
and the output:
result
how am i supposed to use this content input?
or if you know a better for my main question of saving multiple models in the database and their relationships please leave an answer, thanks in advance
I suggest you change your models.py structure to this:
from django.db import models
class Category(models.Model):
category = models.CharField(max_length=20, null=True, blank=True)
created_on = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Categories"
class Picture(models.Model):
image = models.FileField(upload_to='shop/images')
product = models.ForeignKey(to=Products, on_delete=models.CASCADE)
created_on = models.DateTimeField(blank=True, default=datetime.now)
class Product(models.Model):
name = models.CharField(max_length=35, null=False, unique=True)
description = models.CharField(max_length=255)
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.)
main_image = models.FileField(upload_to='shop/images')
more_images = models.ManyToManyField(Pictures, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now=True)
Then in your serializer.py add:
from rest_framework import serializers
from .models import Category, Picture, Product
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = "__all__"
class PictureSerializer(serializers.ModelSerializer):
class Meta:
model = Picture
fields = "__all__"
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = "__all__"
In your views, I suggest you use ViewSets:
views.py
from .models import Category, Picture, Product
from .serializer import CategorySerializer, PictureSerializer, ProductSerializer
from rest_framework import viewsets
# import custom permissions if any
class CategoryViewSet(viewsets.ModelViewSet):
serializer_class = CategorySerializer
queryset = Category.objects.all()
class PictureViewSet(viewsets.ModelViewSet):
serializer_class = PictureSerializer
queryset = Picture.objects.all()
class ProductViewSet(viewsets.ModelViewSet):
serializer_class = ProductSerializer
queryset = Product.objects.all()
permission_classes = [IsModerator]
In your app's urls.py, add the router for your viewsets and it will create the paths for your views automatically:
from django.urls import path
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'category', views.CategoryViewSet, basename='category')
router.register(r'picture', views.PictureViewSet, basename='picture')
router.register(r'product', views.ProductViewSet, basename='product')
urlpatterns = [
path('', include(router.urls)),
]
Changes log:
You do not need to add an ID field to every model, Django does that for you. Unless it's a particular case.
Your database tables are named after your model by default. So no need to specify that too.
I simplified your models' structure to make it cleaner. But it still does what you want it to do.
Django adds an s to create a plural name for every model. So you can name it in singular form unless needed to specify. eg. categories.
The viewsets will reduce your work by providing you with listing and retrieval actions.
To access a specific instance of eg. a product, you will just add a /<product id> after the product listing and creation endpoint.
Note: You have to add the id without the brackets.
I also suggest you go through this DRF tutorial. It will improve your understanding of Django REST framework.

Django - Models category error in Admin: “Select a valid choice. That choice is not one of the available choices”

So I am trying to build a recipe field in django-admin. I have a Model category called Meals and I have categories to chose from on the admin page but as soon as I try to save my entry, I get the Select a valid choice. Breakfast is not one of the available choices & prevents me from saving anything.
I've tried moving the daily_meals value from Recipe Field into Meal Category and uncommenting the first meal and commenting the second value in Recipe Field.
How do I create new meals in the meals category, just like the food category, while already having pre-saved meals in the list?
Meal categories
admin error after save
Here is my code:
Models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
# Food Category
class Category(models.Model):
name = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
# Meal Category
class Meal(models.Model):
name = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique=True)
class Meta:
ordering = ('name',)
verbose_name = 'meal'
verbose_name_plural = 'meals'
def __str__(self):
return self.name
# Recipe Field
class Recipe(models.Model):
title = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE,)
daily_meals = (
('Breakfast','Breakfast'),
('Brunch','Brunch'),
('Elevenses','Elevenses'),
('Lunch','Lunch'),
('Tea','Tea'),
('Supper','Supper'),
('Dinner','Dinner'),
)
# meal = models.CharField(max_length=100, choices = daily_meals)
meal = models.ForeignKey(Meal, choices = daily_meals, on_delete=models.CASCADE,)
ingredients = models.TextField(blank=True)
directions = models.TextField(blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
Admin.py
from django.contrib import admin
from .models import Recipe, Category, Meal
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
prepopulated_fields = {'slug': ('name',)}
admin.site.register(Category, CategoryAdmin)
class MealAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
prepopulated_fields = {'slug': ('name',)}
admin.site.register(Meal, MealAdmin)
class RecipeAdmin(admin.ModelAdmin):
readonly_fields = ('created',)
admin.site.register(Recipe, RecipeAdmin)
ForeignKey fields do not typically use choices. The choices are generated from what is in the database. If you construct choices yourself, choice[0] must be a valid primary key of the Meal table. That being said, you can use the limit_choices_to parameter if you want restrict the choices to certain instances:
daily_meals = ['Breakfast', 'Brunch', 'Elevenses', 'Lunch', 'Tea', 'Supper', 'Dinner']
# ...
meal = models.ForeignKey(Meal, limit_choices_to={'name__in': daily_meals}, on_delete=models.CASCADE,)

Save Django Form wizard data to three models with related fields

I am working on a project that requires use of form wizard to populate three related models. The first model - Listing - has general data which has a OneToOneField relationship with the second model (Property). The Listing model also has a many to many relationships with the third model (ListingImages). In general, I am using 4 forms in the wizard. Here is the models definition
models.py
class Listing(models.Model):
listing_type_choices = [('P', 'Property'), ('V', 'Vehicle'), ('B', 'Business/Service'), ('E', 'Events')]
listing_title = models.CharField(max_length=255)
listing_type = models.CharField(choices=listing_type_choices, max_length=1, default='P')
status = models.BooleanField(default=False)
featured = models.BooleanField(default=False)
city = models.CharField(max_length=255, blank=True)
location = PlainLocationField(based_fields=['city'], zoom=7, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expires_on = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(User,
on_delete=models.CASCADE, editable=False, null=True, blank=True
)
listing_owner = models.ForeignKey(User,
on_delete=models.CASCADE, related_name='list_owner'
)
def __str__(self):
return self.listing_title
def get_image_filename(instance, filename):
title = instance.listing.listing_title
slug = slugify(title)
return "listings_pics/%s-%s" % (slug, filename)
class ListingImages(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
image_url = models.ImageField(upload_to=get_image_filename,
verbose_name='Listing Images')
main_image = models.BooleanField(default=False)
class Meta:
verbose_name_plural = "Listing Images"
def __str__(self):
return f'{self.listing.listing_title} Image'
class Property(models.Model):
sale_hire_choices = [('S', 'Sale'), ('R', 'Rent')]
fully_furnished_choices = [('Y', 'Yes'), ('N', 'No')]
listing = models.OneToOneField(Listing, on_delete=models.CASCADE)
sub_category = models.ForeignKey(PropertySubCategory, on_delete=models.CASCADE)
for_sale_rent = models.CharField(choices=sale_hire_choices, max_length=1, default=None)
bedrooms = models.PositiveIntegerField(default=0)
bathrooms = models.PositiveIntegerField(default=0)
rooms = models.PositiveIntegerField(default=0)
land_size = models.DecimalField(max_digits=10, decimal_places=2)
available_from = models.DateField()
car_spaces = models.PositiveIntegerField(default=0)
fully_furnished = models.CharField(choices=fully_furnished_choices, max_length=1, default=None)
desc = models.TextField()
property_features = models.ManyToManyField(PropertyFeatures)
price = models.DecimalField(max_digits=15, decimal_places=2)
currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Here is the forms.py
from django import forms
from .models import Listing, Property, Vehicle, Business, ListingImages
from django.forms import modelformset_factory
class ListingDetails(forms.ModelForm):
class Meta:
model = Listing
fields = ['listing_title', 'city', 'location']
class PropertyDetails1(forms.ModelForm):
class Meta:
model = Property
fields = ['sub_category', 'for_sale_rent', 'bedrooms', 'bathrooms',
'rooms', 'land_size', 'available_from', 'car_spaces', 'fully_furnished',
'desc', 'currency', 'price'
]
class PropertyDetails2(forms.ModelForm):
class Meta:
model = Property
fields = ['property_features']
class ListingImagesForm(forms.ModelForm):
image_url = forms.ImageField(label='Listing Image',
widget=forms.ClearableFileInput(attrs={'multiple': True}),
required=False
)
class Meta:
model = ListingImages
fields = ['image_url']
ImageFormSet = modelformset_factory(ListingImages, form=ListingImagesForm, extra=3)
views.py
from django.shortcuts import render, redirect
import os
from .forms import ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm
from .models import ListingImages, Listing, Property
from formtools.wizard.views import SessionWizardView
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.forms import modelformset_factory
from django.contrib import messages
from django.http import HttpResponseRedirect, HttpResponse
from django.forms.models import construct_instance
class PropertyView(SessionWizardView):
# formset = ImageFormSet(queryset=Images.objects.none())
template_name = "listings/create_property.html"
form_list = [ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media'))
def done(self, form_list, **kwargs):
listing_instance = Listing()
property_instance = Property()
listing_instance.created_by = self.request.user
listing_instance.listing_owner = self.request.user
listing_instance.listing_type = 'P'
for form in form_list:
listing_instance = construct_instance(form, listing_instance, form._meta.fields, form._meta.exclude)
property_instance = construct_instance(form, property_instance, form._meta.fields, form._meta.exclude)
listing = listing_instance.save()
property_instance.listing = listing
property_instance.save()
return HttpResponse('data saved successfully')
The problem that I am facing is that I am able to save the Listing model, but getting its primary id and using it to save the Property model is the problem. Again, the ListingImages model stores images related to the Listing model. How do I save these models to database considering that they are multiple?
What's wrong is that as described here, model.save() does not return the saved object, but None.
So the last few lines of the above code should be
listing_instance.save()
property_instance.listing = listing_instance
property_instance.save()
return HttpResponse('data saved successfully')
Ditto saving a set of listing_images would be something like
for li_obj in listing_image_instances:
li_obj.listing = listing_instance # saved above
li_obj.save()

AttributeError: 'File' object has no attribute 'model'

I a having following setup in which my Model classes look like following
from django.db import models
from User.models import UserProfile
class Advertisement(models.Model):
owner = models.ForeignKey(UserProfile, related_name='advertisements', on_delete=models.CASCADE)
title = models.CharField(max_length=500, blank=False, default='')
location = models.CharField(max_length=300, blank=False, default='')
rent = models.IntegerField(blank=False)
status = models.CharField(max_length=100, blank=False)
no_of_bedrooms = models.IntegerField(blank=False)
no_of_bathrooms = models.IntegerField(blank=False)
posted_by = models.CharField(max_length=100, blank=True)
date_ad_posted = models.DateTimeField(null=True, blank=True)
contact = models.CharField(max_length=13, blank=False)
description = models.CharField(max_length=1000, blank=False)
security_deposit = models.IntegerField(default=0, blank=False)
def save(self, *args, **kwargs):
super(Advertisement, self).save(*args, **kwargs)
class File(models.Model):
Ad = models.ForeignKey(Advertisement, related_name='ad-photo', on_delete=models.CASCADE)
file = models.FileField(blank=False, null=False)
remark = models.CharField(max_length=20)
timestamp = models.DateTimeField(auto_now_add=True)
and my serializers look like following
class AdvertisementSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
class Meta:
model = Advertisement
fields = ('id', 'owner', 'title', 'location', 'rent', 'status', 'no_of_bedrooms', 'no_of_bathrooms',
'date_ad_posted', 'posted_by', 'contact', 'description')
kwargs = {
'url': {'view_name': 'advertisement-detail'}
}
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = ('Ad', 'file', 'remark', 'timestamp')
i am registering my model in admin.py in following way
from django.contrib import admin
from .models import Advertisement, AdContract, File
class FileInline(admin.TabularInline):
model = File
class PropertyAdmin(admin.ModelAdmin):
inlines = [FileInline, ]
admin.site.register(Advertisement, File)
admin.site.register(AdContract)
but upon making migrations i am getting following error
i have tried a few solutions i got after googling but the error is still there. Any help is appreciated.
To register multiple models at once you need to pass them as iterable:
admin.site.register([Advertisement, File])
instead of
admin.site.register(Advertisement, File)
admin.site.register is used to register a model class with an admin class.
This is it's function signature:
AdminSite.register(model_or_iterable, admin_class=None, **options)
In you admin.py you're passing File model instead of the your custom admin class: admin.site.register(Advertisement, File)
Try this:
from django.contrib import admin
from .models import Advertisement, AdContract, File
class FileInline(admin.TabularInline):
model = File
class PropertyAdmin(admin.ModelAdmin):
inlines = [FileInline, ]
admin.site.register(File, FileInline)
admin.site.register(AdContract)
I encountered a similar problem. Like you, I was registering the Model, not the admin model.
Try This:
admin.site.register(File, FileInLine)

Django user model, backward look up is not working

I've made a foreign key relationship with django User model, the forward lookup is working fine but when I try to backward is throwing this error:
'QuerySet' object has no attribute 'urlpost_set'
I have also tried the related name! Also note that the Catagory to PostUrl and PostUrl to Catagory is working just fine!
My models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Catagory(models.Model):
title = models.CharField(max_length=15, unique=True)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = 'catagory'
class UrlPost(models.Model):
STATUS_CHOICES = (
('public', 'Public'),
('private', 'Private'),
)
profile = models.ForeignKey(User, related_name='user_post', on_delete=models.CASCADE)
catagory = models.ForeignKey(Catagory, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
url = models.URLField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='public')
note = models.TextField(blank=True)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
class Meta:
ordering = ['-created']
verbose_name_plural = 'url Post'
def __str__(self):
return self.title
You have set related_name='user_post' while defining ForeignKey relation between your User model and UrlPost.
You have to use .user_post.all() instead of .urlpost_set.all() in your queryset.