django rest framework upload image to related model using generic.createAPIView - python

I am scheduling a medical camp at some destination. After reaching the destination i want to upload picture of that destination of mapped destination.
I Created two model for that
One is SiteMap
class SiteMap(models.Model):
MpId = models.IntegerField(verbose_name='MPID')
VisitingDate = models.DateField(default=timezone.now,verbose_name='Date')
Vehical = models.ForeignKey(VehicalMaster,on_delete=models.CASCADE,verbose_name='Vehical')
Doctor = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name='Doctor')
District = models.ForeignKey(District,on_delete=models.CASCADE,verbose_name='District')
Block = models.ForeignKey(Block,on_delete=models.CASCADE,verbose_name='Block')
Panchayat = models.CharField(max_length=120,verbose_name="Panchayat")
Village = models.CharField(max_length=120,verbose_name='Village')
updated = models.DateTimeField(auto_now=True)
And secod one is SiteMapImage
class SiteMapImage(models.Model):
MPID = models.ForeignKey(SiteMap,on_delete=models.CASCADE,verbose_name='MPID')
SiteImage = models.ImageField(default='default.png',upload_to='sitemap/%Y/%m/%d')
Location = models.CharField(max_length=200,verbose_name='Location',null=True,blank=True)
Latitude = models.CharField(max_length=120,verbose_name='Latitude',null=True,blank=True)
Longitue = models.CharField(max_length=120,verbose_name='Longitude',null=True,blank=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.MPID}"
def save(self):
super().save()
siteimg = Image.open(self.SiteImage.path)
if siteimg.height>300 or siteimg.width>300:
output_size = (300,300)
siteimg.thumbnail(output_size)
siteimg.save(self.SiteImage.path)
I have created a serializer class for this. Here is the code.
class SiteMapImageSerializer(serializers.ModelSerializer):
class Meta:
model = SiteMapImage
fields = ['MPID','Location','Latitude','Longitue','SiteImage']
and this is my view.py
class SiteMapImageCreateView(generics.CreateAPIView):
lookup_field = 'SiteMap.pk'
serializer_class = SiteMapImageSerializer
def get_queryset(self):
return SiteMapImage.objects.all()
I don't know what wrong i did. but not working in broweser too.
I upload the error image tooo.

Related

Get Image Field Absolute Path in Django Rest Framework - Non Request Flows

I've a periodic celery task which needs to store representation of a object in a specific json field.
Here is the simplified model structure.
Parent <-- ChildWrapper <-- Child Image
So basically I've a 'ChildImage' model referring to 'ChildWrapper' which in turn refers to 'Parent'.
class Parent(TimeStampedModel):
label = models.CharField(max_length=30, unique=True)
live_content = JSONField(blank=True, null=True)
is_template = models.BooleanField(default=False)
reference_image = models.ImageField(upload_to=get_web_row_reference_image_path, blank=True, null=True)
# Around 8 Other Fields
def __str__(self):
return '%s' % self.label
class ChildWrapper(TimeStampedModel):
name = models.CharField(max_length=25, blank=True, null=True)
row = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='web_column')
order = models.PositiveIntegerField(default=0)
# Around 20 Other Fields
def __str__(self):
return '%s' % self.name
class ChildImage(TimeStampedModel):
image = models.ImageField(upload_to=get_web_image_path)
column = models.ForeignKey(ChildWrapper, on_delete=models.CASCADE, related_name='web_image')
# Around 10 Other Fields
def __str__(self):
return '%s' % self.column
This is the serializers defined for the models.
class ChildImageSerializer(serializers.ModelSerializer):
class Meta:
model = ChildImage
fields = '__all__'
class ChildWrapperSerializer(serializers.ModelSerializer):
web_image = ChildImageSerializer(read_only=True, many=True)
class Meta:
model = ChildWrapper
fields = '__all__'
class ParentSerializer(serializers.ModelSerializer):
web_column = ChildWrapperSerializer(many=True, read_only=True)
class Meta:
model = Parent
fields = '__all__'
Here is the periodic celery task which does the required
#app.task(bind=True)
def update_data(self):
# Get Parent By a condition.
parent = Parent.objects.filter(to_update=True).first()
parent.live_content = None
parent.live_content = ParentSerializer(parent).data
print(parent.live_content)
parent.save()
The above task gets output of child image something like this with imagefield being relative path instead of absolute path.
{
"id": 1
"image": '/api/col/info.jpg'
}
Is there any way to get the absolute path for the image field?
{
"id": 1
"image": "http://localhost:8000/admin/media/api/col/info.jpg"
}
PS:
I cannot pass Request context to serializer as ParentSerializer(parent, context={'request': request}) as there is no request object involved here.
I think you have two ways to resolve this.
First one, is to pass request. You can take this approach:
class ChildImageSerializer(serializers.ModelSerializer):
img_url = serializers.SerializerMethodField()
class Meta:
model = ChildImage
fields = '__all__'
def get_img_url(self, obj):
return self.context['request'].build_absolute_uri(obj.image.url)
class ChildWrapperSerializer(serializers.ModelSerializer):
web_image = serializers.SerializerMethodField()
class Meta:
model = ChildWrapper
fields = '__all__'
def get_web_image(self, obj):
serializer_context = {'request': self.context.get('request') }
children = ChildImage.objects.filter(row=obj)
serializer = ChildImageSerializer(children, many=True, context=serializer_context)
return serializer.data
class ParentSerializer(serializers.ModelSerializer):
web_column = serializers.SerializerMethodField()
class Meta:
model = Parent
fields = '__all__'
def get_web_column(self, obj):
serializer_context = {'request': self.context.get('request') }
children = ChildWrapper.objects.filter(row=obj)
serializer = ChildWrapperSerializer(children, many=True, context=serializer_context)
return serializer.data
Here I am using SerializerMethodField to pass the request on to the next serializer.
Second approach is to use Django Sites Framework(mentioned by #dirkgroten). You can do the following:
class ChildImageSerializer(serializers.ModelSerializer):
img_url = serializers.SerializerMethodField()
class Meta:
model = ChildImage
fields = '__all__'
def get_img_url(self, obj):
return 'http://%s%s%s' % (Site.objects.get_current().domain, settings.MEDIA_URL, obj.img.url)
Update: I totally missed the celery part. For production, I don't think you need to worry as they are in S3, the absolute path should be coming from obj.image.url. And in dev and stage, you can get the absolute path using the given example. So, try like this:
class ChildImageSerializer(serializers.ModelSerializer):
img_url = serializers.SerializerMethodField()
class Meta:
model = ChildImage
fields = '__all__'
def get_img_url(self, obj):
if settings.DEBUG: # debug enabled for dev and stage
return 'http://%s%s%s' % (Site.objects.get_current().domain, settings.MEDIA_URL, obj.img.url)
return obj.img.url
Alternatively, there is a way to get request using django-crequest in celery, but I am not sure if its convenient to you.
Got it working,
Added MEDIA_URL to my settings file as mentioned here.
It seems DRF uses MEDIA_URL as a default prefix for urls(FileField & ImageField), even for non request/response flows.
Since I had a different settings file for staging, development and production it was easier for me to set different URLs for each environment.
Even though I'm not using 'django-versatileimagefield' library, the suggestion there still worked.
I solved the problem by adding , context={'request': request} in view.
serializer = Business_plansSerializer(business_plans[start:end], many=True, context={'request': request})
Another solution is hard code the host:
from django.conf import settings
IMG_HOST = {
'/home/me/path/to/project': 'http://localhost:8000',
'/home/user/path/to/project': 'https://{AWS_HOST}',
}[str(settings.BASE_DIR)]
class ChildImageSerializer(serializers.ModelSerializer):
image = serializers.SerializerMethodField()
def get_image(self, obj):
if obj.image:
return IMG_HOST + obj.image.url
class Meta:
model = ChildImage
fields = '__all__'

Why my CreateAPIView does not show Product and store category field?

I am designing an API for listing stores and create store. I could list store but while designing for creating store I am not getting product and store category field inspite of calling all Product and Store category serializer in Store Serializer.
My shortened models look like
class Merchant(models.Model):
user = models.ForeignKey(User)
phone = models.PositiveIntegerField(null=True,blank=True)
class Store(models.Model):
merchant = models.ForeignKey(Merchant)
name_of_legal_entity = models.CharField(max_length=250)
class Product(models.Model):
store = models.ForeignKey(Store)
image = models.ForeignKey('ProductImage',blank=True,null=True)
name_of_product = models.CharField(max_length=120)
class ProductImage(models.Model):
image = models.ImageField(upload_to='products/images/')
class StoreCategory(models.Model):
product = models.ForeignKey(Product,null=True, on_delete=models.CASCADE,related_name="store_category")
store_category = models.CharField(choices=STORE_CATEGORIES, default='GROCERY', max_length=10)
Serializer.py
class ProductImageSerializer(ModelSerializer):
class Meta:
model = ProductImage
fields = ( 'id','imageName', )
class ProductSerializers(ModelSerializer):
image = ProductImageSerializer(many=False,read_only=True)
class Meta:
model = Product
fields=('id','image','name_of_product','description','price','active',)
class StoreCategorySerializer(ModelSerializer):
product = ProductSerializers(read_only=True)
class Meta:
model = StoreCategory
class StoreSerializer(ModelSerializer):
# url = HyperlinkedIdentityField(view_name='stores_detail_api')
store_categories = StoreCategorySerializer(many=True)
merchant = MerchantSerializer(read_only=True)
class Meta:
model = Store
fields=("id",
"merchant",
"store_categories",
"name_of_legal_entity",
"pan_number",
"registered_office_address",
"name_of_store",
)
Views.py
class StoreCreateAPIView(CreateAPIView):
queryset = Store.objects.all()
serializer_class = StoreSerializer
parser_classes = (FormParser,MultiPartParser,)
def put(self, request, filename, format=None):
print('first put works')
file_obj = request.data['file']
print ('file_obj',file_obj)
return Response(status=204)
def perform_create(self, serializer):
print('then perform works')
serializer.save(user=self.request.user)
Here is the screenshot of how it looks
Why it is not showing Merchant, Product and Store Category in the form?
Remove read_only=True from the serializers that you wanna create entry of.
Like:
product = ProductSerializers(read_only=True)
should be
product = ProductSerializers()
read_only will prevent it from written therefore i wont be in the outcome.

Reference objects using foreign keys in Django forms

I did a lot of search for an issue I am facing, but couldn't find a suitable solution. I am a Django beginner
I am creating a project in which an User will be able to ask a wish, and other users will be assigned that wish, which they can then draw and submit.
I created views for asking and getting a wish, but facing issue while submitting the sketch for the wish. I do not know how to show only those wishes in the add_sketch form for the current user and then update the sketch model with this new sketch.
Right now I am just using a charField for the uploaded sketch. Here is the code
models.py
class Wish(models.Model):
content = models.CharField(max_length=500)
wisher = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add=True)
locked = models.BooleanField(default=False)
class Meta():
verbose_name_plural = 'Wishes'
def __unicode__(self):
return self.content
class Sketch(models.Model):
wish = models.ForeignKey(Wish)
sketcher = models.ForeignKey(User)
image_temp = models.CharField(max_length=128)
likes = models.IntegerField(default=0)
assigned_on = models.DateTimeField(auto_now_add=True)
submitted_on = models.DateTimeField(auto_now=True)
class Meta():
verbose_name_plural = 'Sketches'
def __unicode__(self):
return "Sketch for \""+ self.wish.content + "\""
views.py
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST)
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm()
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
And here is the forms.py
class GetWishForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Wish.objects.filter(pk__in = Wish.objects.filter(locked=False)[:3].values_list('pk')), initial=0)
class Meta:
model = Sketch
fields = ('wish',)
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
UPDATE:
I edited the code according to #almalki's suggestion
forms.py
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
def __init__(self, *args, **kwargs):
super(SketchForm, self).__init__(*args, **kwargs)
self.fields['wish'].queryset = kwargs.pop('wish_qs')
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
views.py
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST)
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm(wish_qs=Wish.objects.filter(wisher=request.user))
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
I still get the error init() got an unexpected keyword argument 'wish_qs'
UPDATE 2:
forms.py remains same as above, here is what I think the views.py should be
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST, wish_qs=Sketch.objects.filter(sketcher=request.user))
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm(wish_qs=Sketch.objects.filter(sketcher=request.user))
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
When I choose a wish, and click submit, the error is: annot assign "": "Sketch.wish" must be a "Wish" instance.
I know this is because the model is expecting a Wish instance, but we are giving a Sketch instance, but I don't know how to achieve what I need. I think some change has to be made in the models.py, connecting Wish and Sketch reversibly.
You need to override the field query set in form initialization:
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
def __init__(self, *args, **kwargs):
wish_qs = kwargs.pop('wish_qs')
super(SketchForm, self).__init__(*args, **kwargs)
self.fields['wish'].queryset = wish_qs
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
And in your view, you need to pass a queryset filtered based on current logged in user:
sketch_form = SketchForm(request.POST, wish_qs=Wish.objects.filter(wisher=request.user))
and:
sketch_form = SketchForm(wish_qs=Wish.objects.filter(wisher=request.user))

Django rest framework saving thumbnail image

I've recently started developing with django + python and everything was going very smooth until I got stuck into a problem that probably is very simple but I can not solve with my inexperience with the framework/language.
I'm receiving an JSON object through an HTTP Request which contains some data and 2 pictures. Prior to those 2 pictures I wanted to save a thumbnail from one of them but I don't see to get that task done. I can save all the data easily including the 2 images. But I can not see to find a way to generate a way an have that in the DB also, as well the folder structure that I want.
My folders should look like:
pictures
user
originals
processed
thumbnails
otherUser
originals
processed
thumbnails
My goal is: Receive 2 pictures, create a thumbnail from one of them and them save all 3 pictures in 3 separate folders and the path to the Database.
Here's how my model code looks like.
class SomeData(models.Model):
owner = models.ForeignKey('auth.User', related_name='canopeo_data')
adjustments = models.CharField(max_length=10)
latitude = GeopositionField()
longitude = GeopositionField()
notes = models.TextField(null=True, blank=True)
original_image = models.ImageField(upload_to=original_image, max_length=255, blank=True)
processed_image = models.ImageField(null=False, upload_to=processed_image, max_length=255)
thumbnail_image = models.ImageField(null=False, upload_to=thumbnail_image, max_length=255)
date_time = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('date_time',)
def save(self, *args, **kwargs):
super(SomeData, self).save(*args, **kwargs)
def original_image(self, filename):
url = "pictures/%s/originals/%s" % (self.owner.username, filename)
return url
def processed_image(self, filename):
url = "pictures/%s/processed/%s" % (self.owner.username, filename)
return url
def thumbnail_image(self, filename):
url = "pictures/%s/thumbnail/%s" % (self.owner.username, filename)
return url
Serializer code...
class SomeDataSerializer(serializers.HyperlinkedModelSerializer):
#url = serializers.HyperlinkedRelatedField(view_name='data', format='html')
owner = serializers.Field(source='owner.username')
thumbnail_image = serializers.Field(source='original_image')
class Meta:
model = SomeData
fields = ('url', 'adjustments', 'latitude', 'longitude', 'notes', 'original_image', 'processed_image',)
View code...
class SomeDataViewSet(viewsets.ModelViewSet):
queryset = SomeData.objects.all()
serializer_class = SomeDataSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.owner = self.request.user
I've tried many things such as easy_thumbnails, sorl_thumbnail, and some pre made methods on how to do it.. but I can't see to find a solution specific for what I've been wanting.
Thank you very much!
Looks like you have mistake in definition of the SomeDataSerializer. In model SomeData field original_image defined as ImageField, but in serialiser it's just Field, not ImageField. You should use correct field type:
class SomeDataSerializer(serializers.HyperlinkedModelSerializer):
#url = serializers.HyperlinkedRelatedField(view_name='data', format='html')
owner = serializers.Field(source='owner.username')
thumbnail_image = serializers.ImageField(source='original_image')
...

Getting Request in admin.py

I have a model like this:
class Item(models.Model):
code = models.CharField(max_length=200, unique=True)
barcode = models.CharField(max_length=300)
desc = models.CharField('Description',max_length=500)
display_desc = models.CharField('Description',max_length=500,
blank=True, null=True)
price = models.FloatField()
discountable = models.BooleanField(blank=True, default=False)
image_path = models.CharField(max_length=300,unique=True, blank=True, null=True)
def __unicode__(self):
return self.code + ' : ' + self.desc
But unfortunately, I don't want to store the item's image in the database, instead I want to store the image path in the server in the image_path column.
So, I created a custom admin.py for this object so that I could edit/insert the object thru the Django admin module. As a result, below is the customized admin.py
class ItemAdminForm(forms.ModelForm):
file_upload = forms.FileField(required=False)
class Meta:
model = Item
def __init__(self, *args, **kwargs):
super(ItemAdminForm, self).__init__(*args,**kwargs)
#if kwargs.has_key('instance'):
# instance = kwargs['instance']
# self.initial['file_upload'] = instance.file_upload
def handle_uploaded_file(f):
destination = open('D:/Project/pensel/penselshop/static/picture', 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
return f.name
def save(self,commit=True):
name = None
extension = None
#error here! How could I get the request?
miniform = ItemAdminForm(request.POST, request.FILES)
if miniform.is_valid():
name = handle_uploaded_file(request.FILES['file_upload'])
extension = name.split('.')[1]
model = super(ItemAdminForm, self).save(commit=False)
model.image_path = '/static/picture/' + model.code + extension
if commit:
model.save()
return model
However, during processing the save() function, I noticed that there is an error in getting the request. How can I get the request so that I could retrieve the file? I noticed that the request is automatically added in views.py, but not admin.py
Django's ImageField and FileField fields don't actually store the image in the database either. All that is stored in the database is the path, which you can control yourself. The actual image file is stored on the filesystem. So I'm not sure why you are going to all this trouble...?
But to answer your question of how to get the request in the admin, you can override ModelAdmin.save_model().

Categories

Resources