Django Form Form to fill multiple models at once - django-forms

I have a few models that are all coming together in the Thema model. But I need a form to fill them all at once from the thema form. My problem is that when I create a form from the model it just creates a selection of for example tags that already exist in the database. But I need to create new ones in the form as well at the same time.
I want the user to fill in stuff in the Thema, upload a file to the file Model, create or select tags in the tags model and select or create into the Categories model.
The Problem is how can I bring all of them together in one Form? Also the Thema Model uses a related field to the Bilder model but I canĀ“t upload new pics in the Thema form which are then stored in the Bilder Model.
This is the model.py
from django.db import models
from django.urls import reverse
from datetime import datetime
class Category(models.Model):
name = models.CharField(max_length=250, help_text='Kategorie', blank=True)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Link(models.Model):
alias = models.CharField(max_length=250, help_text='Linkalias', blank=True)
link = models.URLField(("Links zum Thema"), max_length=128, db_index=True, unique=True, blank=True)
def __str__(self):
return self.alias
class File(models.Model):
name = models.CharField(max_length=250, help_text='Dateiname', blank=True)
upload = models.FileField(upload_to ='uploads/', null=True, blank=False)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=250, help_text='tagname', blank=True)
def __str__(self):
return self.name
class Thema(models.Model):
publication_date = models.DateField(verbose_name="Date the Post was published.", default=datetime.now)
title = models.CharField(max_length=70, help_text="The title of the Post")
text = models.TextField(max_length=5000,default='text 1')
text2 = models.TextField(max_length=5000,default='text 2')
text3 = models.TextField(max_length=5000,default='text 3')
# Relation Fields
category = models.ForeignKey(Category, on_delete=models.CASCADE)
links = models.ManyToManyField(Link)
files = models.ManyToManyField(File)
tags = models.ManyToManyField(Tag)
def get_absolute_url(self):
return reverse('thema-detail', kwargs={'pk' : self.pk})
def __str__(self):
return self.title
class Bilder(models.Model):
name = models.CharField(max_length=250, help_text='Bildname', blank=True)
beschreibung = models.CharField(max_length=400, help_text='Kurzbschreibung', blank=True)
thema = models.ForeignKey(Thema, on_delete=models.CASCADE, related_name='bilder', null=True)
image = models.ImageField(null=True, blank=True, upload_to='images/')
class Meta:
verbose_name_plural = "Bilder"
def __str__(self):
return self.name
and this is my attempt to create the form:
from django import forms
from django.forms import formset_factory
from .models import Thema, Bilder, Category, Link, File, Tag
from django.forms import formset_factory
class BilderForm(forms.ModelForm):
class Meta:
model = Bilder
fields = ['name', 'beschreibung', 'image']
BilderFormSet = formset_factory(BilderForm, extra=1)
class ThemaForm(forms.ModelForm):
bilder_formset = BilderFormSet()
class Meta:
model = Thema
fields = ['publication_date', 'title', 'text', 'text2', 'text3', 'category', 'links', 'files', 'tags']
def __init__(self, *args, **kwargs):
self.bilder_formset = kwargs.pop('bilder_formset', None)
super().__init__(*args, **kwargs)

Related

How to retrieve multiple slugs as url parameters with Django REST Framework DRF RetrieveAPIView?

I am new to DRF. I am very interested in application of generic views. I want to rewrite the following code with generic DRF view RetrieveAPIView:
views.py
class ProductDetail(APIView):
def get_object(self, category_slug, product_slug):
try:
return Product.objects.filter(category__slug=category_slug).get(slug=product_slug)
except Product.DoesNotExist:
raise Http404
def get(self, request, category_slug, product_slug, format=None):
product = self.get_object(category_slug, product_slug)
serializer = ProductSerializer(product)
return Response(serializer.data)
urls.py
urlpatterns = [
path('products/<slug:category_slug>/<slug:product_slug>/',
ProductDetail.as_view())
]
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.slug}/'
class Product(models.Model):
category = models.ForeignKey(
Category, related_name='products', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-date_added',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'{self.category.slug}/{self.slug}/'
How can I use RetrieveAPIView instead of above APIView?
I tried to add a custom mixin, did not help:
class MultipleFieldLookupMixin:
def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
multi_filter = {field: self.kwargs[field] for field in self.lookup_fields}
obj = get_object_or_404(queryset, **multi_filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
class ProductDetail(MultipleFieldLookupMixin, RetrieveAPIView):
queryset = Product.objects.filter(category__slug=category_slug).get(slug=product_slug)
serializer = ProductSerializer
lookup_fields = ['category_slug', 'product_slug']
Does not work.

Field name `get_thumbnail` is not valid for model `Product`

After hours of figuring this one out, having visited hundreds of websites googling this issue. I gained a lot of knownledge, but but the fix for my issue.
Exception Type: ImproperlyConfigured
Exception Value: Field name get_thumbnail is not valid for model Product.
models.py
from io import BytesIO
from PIL import Image
from django.core.files import File
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.slug}/'
class Product(models.Model):
category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-date_added',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.category.slug}/{self.slug}/'
def get_image(self):
if self.image:
return 'http://127.0.0.1:8000' + self.image.url
return ''
def get_thumbnails(self):
if self.thumbnail:
return 'http://127.0.0.1:8000' + self.thumbnail.url
else:
if self.image:
self.thumbnail = self.make_thumbnail(self.image)
self.save()
return 'http://127.0.0.1:8000' + self.thumbnail.url
else:
return ''
def make_thumbnail(self, image, size=(300, 200)):
img = Image.open(image)
img.convert('RGB')
img.thumbnail(size)
thumb_io = BytesIO()
img.save(thumb_io, 'JPEG', quality=85)
thumbnail = File(thumb_io, name=image.name)
return thumbnail
serializers.py
from rest_framework import serializers
from .models import Category, Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = (
"id",
"name",
"get_absolute_url",
"description",
"price",
"get_image",
"get_thumbnail",
)
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Product
from .serializers import ProductSerializer
class LatestProductsList(APIView):
def get(self, request, format=None):
products = Product.objects.all()[0:4]
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
Maybe it's just the method name? I see you named it get_thumbnails instead of get_thumbnail

rest_framework access objects of ManyToManyField in intermediate model

I have a patient model. Each patient can have several "Medical Records" where choices come from models like "BodyPart" or "Medicin":
class Patient(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Examination(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class BodyPart(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Medicin(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class MedicalRecord(models.Model):
patient = models.ForeignKey('Patient', on_delete=models.CASCADE,)
examination = models.ForeignKey('Examination', on_delete=models.CASCADE)
part_of_body = models.ForeignKey('BodyPart', on_delete=models.CASCADE)
medicin = models.ManyToManyField('Medicin')
def __str__(self):
return self.patient.name
Using Inlines we can nicely add Records to each Patient:
class MedicalRecordAdmin(admin.TabularInline):
model = MedicalRecord
extra = 1
class PatientAdmin(admin.ModelAdmin):
model = Patient
inlines = [MedicalRecordAdmin,]
...
admin.site.register(Patient, PatientAdmin)
...
Where I'm failing is (already tried trough, serializers.StringRelatedField(many=True), etc.), how can I serialize these relations from the side of the Patient Model?
# view
class PatientViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = PatientSerializer
queryset = Patient.objects.all()
# serializer
class PatientSerializer(serializers.ModelSerializer):
class Meta:
model = Patient
fields = '__all__'
depth = 3
ends in:
[
{
"id": 1,
"name": "Bert"
}
]
## But I would expect something like:
[
{
"id": 1,
"name": "Bert",
"medical_records":[
[
{
"medicine": ["a","b","c"],
"body_part": "leg",
"examination": "x-ray"
}
],
[
{
"medicine": ["e","f","g"],
"body_part": "head",
"examination": "surgery"
}
],
]
}
]
You have to use a nested serializer and use source fields to achieve the desired result.
Nested relationships- DRF documentation
Somewhat like this :
class Patient(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Examination(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class BodyPart(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Medicin(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class MedicalRecord(models.Model):
patient = models.ForeignKey('Patient', on_delete=models.CASCADE, related_name='patient_med')
examination = models.ForeignKey('Examination', on_delete=models.CASCADE, related_name='examination_med')
part_of_body = models.ForeignKey('BodyPart', on_delete=models.CASCADE, relayted_name='part_of_body_med')
medicin = models.ManyToManyField('Medicin', related_name='medicin_med')
class MedicalRecordSerializer(serializers.ModelSerializer):
body_part = serializers.CharField(source='part_of_body.name', read_only=True)
examination = serializers.CharField(source='examination.name', read_only=True)
medicine = serializers.ListField(source='medicine.name', read_only=True)
class Meta:
model = MedicalRecord
fields = ('body_part', 'examination', 'medicine')
class PatientSerializer(serializers.ModelSerializer):
patient_med = MedicalRecordSerializer(many=True, read_only=True)
class Meta:
model = Patient
fields = '__all__'
depth = 3

Django rest create different objects with serializer

In my project I have created a custom User object with a one to one relation to a Profile object in order to isolate authentication fields.
Profile
class Profile(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
image = models.ImageField(default='/user_images/default.png', upload_to='user_images', blank=True, null=True)
def __str__(self):
return self.first_name + " " + self.last_name
User
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
profile = models.OneToOneField(Profile, related_name="user" ,on_delete=models.CASCADE, default=None, blank=True, null=True)
is_visible = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
My problem now is that i want to create the Profile object with the same request of the User object
View
class CustomView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format='json'):
serializer = CustomSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Serializer
class CustomSerializer(serializers.Serializer):
# User
email = serializers.EmailField(required=True)
password = serializers.CharField(min_length=8, write_only=True)
# Profile
first_name = serializers.CharField(max_length=50)
last_name = serializers.CharField(max_length=50)
image = serializers.ImageField(allow_null=True, max_length=100, required=False)
def create(self, validated_data):
profile = Profile.objects.create(
first_name = validated_data['first_name'],
last_name = validated_data['first_name'],
image = validated_data['image'],
)
user = User.objects.create(
email = validated_data['email'],
profile = profile
)
user.set_password(validated_data['password'])
user.save()
return user
Now in the database everything is created correctly but i receive this error
Got AttributeError when attempting to get a value for field
first_name on serializer CustomSerializer. The serializer field
might be named incorrectly and not match any attribute or key on the
User instance. Original exception text was: 'User' object has no
attribute 'first_name'.
I suppose this means that a serializer can only handle the creation of one model so i'm supposed to handle my case?
Thanks
I assume that your error comes from to_representation() and what you can do is to set write_only=True on first_name, last_name and image fields like this:
first_name = serializers.CharField(max_length=50, write_only=True)
However, a more clean solution would be to use ModelSerialzier for both User and Profile models. If you do so, you'll have something like:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "first_name", "last_name", "image")
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ("id", "email", "profile", [...])
def create(self, validated_data):
...

how to save login user in create() of ModelSerializer of drf?

Models.py
class CustomTarget(models.Model):
target_red = models.CharField(max_length=64, blank=True, null=True)
target_amber = models.CharField(max_length=64, blank=True, null=True)
target_green = models.CharField(max_length=64, blank=True, null=True)
remarks = models.CharField(max_length=256, blank=True, null=True)
flag = models.BooleanField(default=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(ProjectsMaster, on_delete=models.CASCADE)
metric = models.ForeignKey(MetricsMaster, on_delete=models.CASCADE)
class Meta:
db_table = 'custom_target'
unique_together = (('user', 'project', 'metric'),)
Serializers.py
class CustomTargetSerializer(serializers.ModelSerializer):
"""
"""
logger.info("Control entered into CustomTargetSerializer class")
class Meta:
model = CustomTarget
fields = ('id', 'target_red', 'flag', 'user', 'project', 'target_amber', 'target_green','remarks', 'metric')
def create(self, validated_data):
"""
:param validated_data:
:return:
"""
data = CustomTarget(target_red=validated_data["target_red"],
user=User(validated_data["user"]),
project=ProjectsMaster(validated_data["project"]),
target_amber=validated_data["target_amber"],
target_green=validated_data["target_green"],
remarks=validated_data["remarks"],
metric=MetricsMaster(validated_data["metric"]),
flag=True, )
data.save()
return data
views.py
#api_view(["PUT"])
#renderer_classes((JSONRenderer,))
def custom_target(request):
"""
To add new data and update existing data in custom_target table.
:param request:
:return:
"""
logger.info("Control entered into custom_target")
return_dict = dict()
try:
metric_data = request.data
metric_config_obj = matrix_config_filter(metric_data)
if metric_config_obj.exists() is True:
if metric_config_obj[0].flag is True:
custom_serializer = CustomTargetSerializer(data=request.data)
custom_tar_obj = CustomTarget.objects.filter(metric=request.data['metric'])
if custom_tar_obj.exists():
custom_serializer.update(validated_data=request.data, instance=custom_tar_obj)
else:
if custom_serializer.is_valid(raise_exception=ValueError):
custom_serializer.create(validated_data=request.data)
return_dict["msg"] = "success"
status_msg = status.HTTP_200_OK
except Exception as e:
logger.error("Error from custom_target: %s" % e)
return_dict["msg"] = "failed"
status_msg = status.HTTP_400_BAD_REQUEST
return Response(return_dict, status=status_msg)
here,now i am giving hard coded user id,but i want to save user id of the login user in the create() of Serializer.
here user is saved in auth_user table and have foreign key relation with Custom_target table.
I tried so many things but i am not getting the request.user in Serializer.
You could use PrimaryKeyRelatedField along with CurrentUserDefault as
class CustomTargetSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())
# your code
UPDATE-1 : Complete Serializer
class CustomTargetSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())
class Meta:
model = CustomTarget
fields = ('id', 'target_red', 'flag', 'user', 'project', 'target_amber', 'target_green', 'remarks', 'metric')
def create(self, validated_data):
user = validated_data.pop('user')
project = validated_data.po('project')
metric = validated_data.pop('metric')
instance = CustomTarget.objects.create(
user=user,
project=project,
metric=metric,
**validated_data
)
return instance

Resources