How to implement multiple file upload in Django with two models?
I have 1 form but two models that make two forms
models.py
class Ads(models.Model):
title = models.CharField(max_length=85, blank=False)
class Images(models.Model):
ad = models.ForeignKey(Ads, related_name='images', on_delete=models.CASCADE)
image = models.ImageField(blank=True, upload_to='')
thumbnail = models.BooleanField(default=False)
views.py
class CreateAd(CreateView):
model = Ads
form_class = CreateAdForm
success_url = reverse_lazy('index')
forms.py
class CreateAdForm(forms.ModelForm):
class Meta:
model = Ads
fields = ('title',)
class ImageForm(forms.ModelForm):
class Meta:
model = Images
fields = ('image', )
Basically you are looking for django formset
formset is a layer of abstraction to work with multiple forms on the same page.
Related
I'm working on a real estate app and want the listings to show only the first image of the Listing. Currently it is showing all images.
class Image(models.Model):
photo = models.ImageField(blank=True, upload_to=get_image_filename)
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
class ImageSerializerForListingList(serializers.ModelSerializer):
photo = serializers.ImageField()
class Meta:
model = Image
fields = ('photo', )
class ListingListSerializer(serializers.HyperlinkedModelSerializer):
image = ImageSerializerForListingList(many=True, required=False, source='image_set')
class Meta:
model = Listing
fields = ('url', 'address', 'image', )
def get_first_image(self, obj):
image = obj.image_set.all().first()
return ImageSerializerForListingList(image, many=True).data
This is still showing all images,
Any advice on this?
solution
"Although you define the get_first_image method on the ListingListSerializer, you are not using it anywhere.
The methods defined on the serializers only get used implicitly when there is a corresponding SerializerMethodField defined on the serializer."
class ListingListSerializer(serializers.HyperlinkedModelSerializer):
first_image = serializers.SerializerMethodField()
class Meta:
model = Listing
fields = ('url', 'address', 'first_image', )
"""Much like our web app our listing view should only show one Image"""
def get_first_image(self, obj):
image = obj.image_set.first()
return FirstImageSerializer(image).data
Say I have these models:
class House(models.model):
address = models.CharField(max_length=100)
class HouseImage(models.Model):
image = models.ImageField(...)
house = models.ForeignKey(House, related_name='images')
And this serializers:
class HouseImageSerializer(serializers.ModelSerializer):
class Meta:
model = HouseImage
fields = ('image', )
class HouseSerializer(serializers.ModelSerializer):
images = HouseImageSerializer(many=True, required=False)
class Meta:
model = House
fields = ('address', 'images', )
And this view:
class HouseDetails(generics.RetrieveUpdateDestroyAPIView):
serializer_class = HouseSerializer
queryset = House.objects.all()
I am making to calls from my frontend. One creates the House (and it works) and the second one is supposed to send the images. Therefore, in the second call I am making a PATCH request (with axios) and my request.data is {'images': [InMemoryUploadedFile1, InMemoryUploadedFile2,...]}
Questions:
Am I doing correct so far by defining the images field on HouseSerializer? If it's correct, what else should I do? Because I know I need to somehow map each of the image in images list in my request.data to the image field in HouseImage.
Is there a better approach in implementing this?
de facto you did not patch you House model, you try to create new HouseImage, so in my mind best way is new APIView for HouseImage model.
in serializers:
class HouseImageSerializer(serializers.ModelSerializer):
class Meta:
model = HouseImage
fields = ('image', 'house')
# ^^^^^^
in view
class HouseImageDetails(generics.RetrieveUpdateDestroyAPIView):
serializer_class = HouseImageSerializer
queryset = HouseImage.objects.all()
in HouseSerializer you will have extra field house in the images does it matter?
for your case you can try to override the update
class HouseSerializer(serializers.ModelSerializer):
images = HouseImageSerializer(many=True, required=False)
class Meta:
model = House
fields = ('address', 'images', )
def update(self, instance, validated_data):
request = self.context.get('request')
images = request.data.get('images', []) if request else []
for img in images:
data = {'image': img, 'house': instance}
hiSerializer = HouseImageSerializer(data)
if hiSerializer.is_valid():
hiSerializer.save()
return super(HouseSerializer, self).update(instance, validated_data)
I am trying to sort out a specific problem that involve "many2many" relationship using through specification.
I've already tried to use inline_factory but I was not able to sort out the problem.
I have these tables
class Person(models.Model):
id = models.AutoField(primary_key=True)
fullname = models.CharField(max_length=200)
nickname = models.CharField(max_length=45, blank=True)
class Meta:
db_table = 'people'
class Role(models.Model):
role = models.CharField(max_length=200)
class Meta:
verbose_name_plural = 'roles'
db_table = 'roles'
class Study(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
description = models.CharField(max_length=1000)
members = models.ManyToManyField(Person, through='Studies2People')
class Meta:
db_table = 'studies'
class Studies2People(models.Model):
person = models.ForeignKey(Person)
role = models.ForeignKey(Role)
study = models.ForeignKey(Study)
class Meta:
verbose_name_plural = 'studies2people'
db_table = 'studies2people'
unique_together = (('person', 'role', 'study'),)
#forms.py
from .models import Study, Person, Role, Studies2People
class RegisterStudyForm(ModelForm):
class Meta:
model = Study
fields = '__all__'
#View.py
class StudyCreateView(CreateView):
template_name = 'managements/register_study.html'
model = Study
form_class = RegisterStudyForm
success_url = 'success/'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.render_to_response(self.get_context_data(form=form))
The code above creates a form like:
Study.Title
Study.description
List of People
I want to create a form to fill in all fields that involve Studies2People Something like this:
Study.Title
Study.description
Combo(people.list)
Combo(Role.list)
Maybe I should start from Studies2People but I don't know how to show the "inline" forms involved.
Thanks in advance
C.
waiting someone that is able to explain with some examples the relationship m2m with through (model & view), I sorted out my problem in a different way.
I've created three forms.
1 Model Form (study)
2 Form (forms with ModelChoiceField(queryset=TableX.objects.all())
Created a classView to manage the get and post action.(validation form too)
In the post procedure I used "transaction" to avoid "fake" data.
I hope that someone will post an example with complex m2m relationships.
Regards
Cinzia
models
class VideoInfo(models.Model):
user = models.ForeignKey(User)
video_name = models.CharField(max_length=200)
director = models.CharField(max_length=200)
cameraman = models.CharField(max_length=200)
editor = models.CharField(max_length=200)
reporter = models.CharField(max_length=200)
tag = models.TextField()
forms
class LoginForm(forms.Form):
username = forms.CharField(max_length=50)
password = forms.CharField(widget=PasswordInput())
class VideoInfoForm(forms.Form):
class Meta:
model = VideoInfo
fields = ['video_type', 'director', 'cameraman', 'editor', 'reporter', 'tag']
Views:
class Main(View):
'''Index page of application'''
def get(self, request):
model = VideoInfo
form = VideoInfoForm()
return render_to_response('main.html', {'form':form}, context_instance=RequestContext(request))
Calling in template as:
{{form.as_p}}
The form is not showing up but if I use LoginForm it's showing up. what am I doing wrong?
Change:
class VideoInfoForm(forms.Form):
To:
class VideoInfoForm(forms.ModelForm):
As you want to use model form, your definition of the form is not correct.
Change
class VideoInfoForm(forms.Form):
to
class VideoInfoForm(forms.ModelForm):
# ------------------^ use ModelForm not Form
Sidenote:
Instead of long list of fields use exclude to just list fields that are not needed.
class VideoInfoForm(forms.ModelForm):
class Meta:
model = VideoInfo
exclude = ['user',]
I am working on multiple django sites and have been limited in making my project look nice for clients.
For example in the same app I have two models images and image galleries. It would be so much nicer to just have an admin entry for galleries and in that a table of images.
That's exactly what InlineModelAdmin is for. Taken a models.py like this:
class Gallery(models.Model):
name = models.CharField(max_length=100)
class Image(models.Model):
image = models.ImageField()
gallery = models.ForeignKey(Gallery)
You create an admin.py like this and only register an admin class for the Gallery:
class ImageInline(admin.TabularInline):
model = Image
class GalleryAdmin(admin.ModelAdmin):
inlines = [ImageInline]
admin.site.register(Gallery, GalleryAdmin)
This is my solution thanks to Dirk's help.
from django.db import models
PHOTO_PATH = 'media_gallery'
class Gallerys(models.Model):
title = models.CharField(max_length=30, help_text='Title of the image maximum 30 characters.')
slug = models.SlugField(unique_for_date='date', help_text='This is automatic, used in the URL.')
date = models.DateTimeField()
class Meta:
verbose_name_plural = "Image Galleries"
ordering = ('-date',)
def __unicode__(self):
return self.title
class Images(models.Model):
title = models.CharField(max_length=30, help_text='Title of the image maximum 30 characters.')
content = models.FileField(upload_to=PHOTO_PATH,blank=False, help_text='Ensure the image size is small and it\'s aspect ratio is 16:9.')
gallery = models.ForeignKey(Gallerys)
date = models.DateTimeField()
class Meta:
verbose_name_plural = "Images"
ordering = ('-date',)
def __unicode__(self):
return self.title
import models
from django.contrib import admin
class ImageInline(admin.TabularInline):
model = Images
class GallerysAdmin(admin.ModelAdmin):
list_display = ('title', 'date', 'slug')
inlines = [ImageInline]
admin.site.register(models.Gallerys,GallerysAdmin)