How to upload multiple image with django rest api - python

I want to know how i can upload multiple image (list) in my views and get a Response like this :
{
"id":1,
"title":"text",
"roms":2,
"image":[
"http:localhost:8000/media/images/houses/image1.png",
"http:localhost:8000/media/images/houses/image2.png",
"http:localhost:8000/media/images/houses/image3.png",
]
}
I have tried this way :
models.py
from django.db import models
class House(models.Model):
title = models.CharField(max_length=255)
rooms = models.IntegerField()
images = models.FileField(upload_to="images/houses/")
def __str__(self):
return self.title
serializers.py*
from rest_framework import serializers
from .models import House
class HouseSerializer(serializers.ModelSerializer):
image = serializers.ListField(max_length=None,child=serializers.FileField)
class Meta:
model = House
fields = '__all__'
views.py
from rest_framework import viewsets
from .models import House
from .serializers import HouseSerializer
class HomeViewSet(viewsets.ModelViewSet):
queryset = House.objects.all()
serialiezer_class = HouseSerializer
But it didn't work, i don't know how to write serializer and views to do this, any body help please.
Thanks in advance

Upload multi img to 1 post on Django ? How do that?
First of all you have to change your models. Then you need to add to this model by turning on the photos loaded with the for loop to your image model. I do not know exactly how to do this event with the rest api, but this is how it is done under normal conditions.

Related

How to post multiple images in a directory through the django rest framework in one click

I am trying to use the django rest framework to carry out multiple POST events once I point it to a directory. The idea is that I will specify the country and a directory containing multiple images. Then the API must go to the directory, and post each image in that directory to the database along with more details available from the filename of each image. Following some pointers from the internet, I managed to build out a little model and framework that looks like it could work except for one thing. The default rest API page doesn't support the HTML input of lists.
I would like to know if my time will be better spent trying to fit the API page to my requirements or if I should go about building my own HTML interface.
Or to be more direct
What is the fastest (and simplest) way for me to upload multiple images (each image is a separate instance in the db) into a database using Django and the Django REST Framework?
models.py:
from django.db import models
# Create your models here.
class Tiles(models.Model):
image = models.ImageField(blank=True, null=True)
lat = models.FloatField(blank=True, null=True)
lng = models.FloatField(blank=True, null=True)
country = models.CharField(blank=True, null=True, max_length=10)
solar = models.FloatField(blank=True, null=True)
wind = models.FloatField(blank=True, null=True)
HV = models.FloatField(blank=True, null=True)
class Meta:
verbose_name_plural = "Tiles"
views.py
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import FileListSerializer
from rest_framework import parsers
from .models import Tiles
# Create your views here.
def index(request):
return render(request, 'main/index.html')
class TileViewSet(viewsets.ModelViewSet):
serializer_class = FileListSerializer
parser_classes = (parsers.MultiPartParser, parsers.FormParser,)
queryset = Tiles.objects.all()
serializers.py
from rest_framework import serializers
from .models import Tiles
import mercantile
class FileListSerializer(serializers.Serializer):
image = serializers.ListField(
child=serializers.FileField(max_length=100000,
allow_empty_file=False,
use_url=False)
)
country = serializers.CharField(max_length=10)
# The create function below overrides the Django default create function to populate multiple fields using the image name
def create(self, validated_data):
country = validated_data.pop('country')
image = validated_data.pop('image')
for img in image:
path = img.path
path = path.split('/')[-1]
path = path.split('.')[0]
x, y, z = path.split('-')
coords = mercantile.ul(int(x), int(y), int(z))
tile = Tiles.objects.create(image=img, country=country, lat=coords.lat, lng=coords.lng, **validated_data)
return tile
class TileSerializer(serializers.ModelSerializer):
class Meta:
model = Tiles
fields = '__all__'
Screenshot of the resulting API page:
I've found a solution to this problem. Hope this helps someone down the line. In serializers.py I changed my ListField to a FilePathField that can take in directories as input.
New serializers.py:
from rest_framework import serializers
from .models import Tiles
import mercantile
import os
BASE_DIR = os.path.dirname((os.path.dirname(os.path.abspath(__file__))))
class FileListSerializer(serializers.Serializer):
# The following FilePathField is named image but refers to a directory containing multiple images
image = serializers.FilePathField(path=(os.path.join(BASE_DIR, 'opt_rec_master_img_dir')), allow_files=False, allow_folders=True)
country = serializers.CharField(max_length=10)
# The following variables for each instance are generated automatically upon clicking create
# They are included here so that the DRF auto generated json output includes this information
lat = serializers.FloatField(read_only=True)
lng = serializers.FloatField(read_only=True)
solar = serializers.FloatField(read_only=True)
wind = serializers.FloatField(read_only=True)
HV = serializers.FloatField(read_only=True)
# This function overrides the default create function to populate multiple fields given a directory full of images.
def create(self, validated_data):
country = validated_data.pop('country')
image = validated_data.pop('image')
directory = image.split('/')[-1]
for img in os.listdir(image):
path = img.split('.')[0]
x, y, z = path.split('-')
coords = mercantile.ul(int(x), int(y), int(z))
tile = Tiles.objects.create(image=os.path.join('opt_rec_master_img_dir', directory, img), country=country, lat=coords.lat, lng=coords.lng)
return tile
class Meta:
model = Tiles
views.py
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import FileListSerializer
from .models import Tiles
from django.core.files.images import ImageFile
from rest_framework.utils.encoders import JSONEncoder
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
# Create your views here.
# def index(request):
# return render(request, 'main/index.html')
#
# JSONEncoder and JSONRenderer have been overriden here to serialize the image path rather than image byte data
class OREncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, ImageFile):
return obj.path
return super().default(obj)
class ORRenderer(JSONRenderer):
encoder_class = OREncoder
class TileViewSet(viewsets.ModelViewSet):
serializer_class = FileListSerializer
queryset = Tiles.objects.all()
renderer_classes = [ORRenderer, BrowsableAPIRenderer]

Does Django allow one to define different set of fields for each document in MongoDB?

Django and MongoDB
Supporting a different set of fields for each document in a collection is one of MongoDB's features. It allows you to store similar data, but with different properties in the same collection.
for example:
{
_id: ObjectId("51156a1e056d6f966f268f81"),
type: "Article",
author: "Derick Rethans",
title: "Introduction to Document Databases with MongoDB",
date: ISODate("2013-04-24T16:26:31.911Z"),
body: "This arti…"
},
{
_id: ObjectId("51156a1e056d6f966f268f82"),
type: "Book",
author: "Derick Rethans",
title: "php|architect's Guide to Date and Time Programming with PHP",
isbn: "978-0-9738621-5-7"
}
Django dose not support Non-Relational data base like mongodb by default, but there are some lib's for this purpose. for example Django MongoDB Engine is a MongoDB backend for Django.
MongoDB allow to use different set of fields for each document in a collection, but in django you have to define models.py:
from django.db import models
from djangotoolbox.fields import ListField
class Post(models.Model):
title = models.CharField()
text = models.TextField()
tags = ListField()
comments = ListField()
the Question is: is there any way to define different set of fields for each document in a collection in MongoDB, when using Django ?
The Alternative
I like using django-mongoengine as it makes things clearer when dealing with MongoDB models.
For example, you can create structured Documents that are going to be transformed into models or EmbeddedDocument`s that are structured documents to be used in an already existed model.
from django_mongoengine import Document, EmbeddedDocument, fields
class Comment(EmbeddedDocument):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
author = fields.StringField(verbose_name="Name", max_length=255)
email = fields.EmailField(verbose_name="Email")
body = fields.StringField(verbose_name="Comment")
class Post(Document):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
title = fields.StringField(max_length=255)
slug = fields.StringField(max_length=255, primary_key=True)
comments = fields.ListField(
fields.EmbeddedDocumentField('Comment'), blank=True,
)
The Answer
So for your case what you need to use is Dynamic document schemas that work in the same way as Document but any data/attributes set to them will also be saved.
class Page(DynamicDocument):
title = StringField(max_length=200, required=True)
# Create a new page and add tags
>>> page = Page(title='Using MongoEngine')
>>> page.tags = ['mongodb', 'mongoengine']
>>> page.save()
>>> Page.objects(tags='mongoengine').count()
>>> 1
I struggled this problem in user profile page. This is my solution.
model.py
from django.contrib.auth.models import User
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,related_name="user_profile")
fullname = models.CharField(max_length=100,verbose_name="full name")
about = models.CharField(max_length=300,blank=True,null=True)
hobies = models.CharField(max_length=200,blank=True)
recent_aktivity = models.CharField(max_length=150,verbose_name="recent activity",null=True)
photo = models.ImageField(blank=True,null=True,upload_to="images/")
recent_badges = models.CharField(max_length=100,verbose_name="recent badges",null=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True,null=True,verbose_name="phone number")
website_url = models.URLField(blank=True,null=True,verbose_name="company website")
projects =models.CharField(max_length=200,blank=True,null=True)
bio = models.CharField(max_length=300,blank=True,null=True)
def __str__(self):
return f'{self.user.username}-ProfileModel'
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
#receiver(post_save,sender=User)
def update_user_profile(sender,instance,created,**kwargs):
if created:
profile = Profile.objects.create(user =instance)
app.py
from django.apps import AppConfig
class ProfileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Profile'
def ready(self):
import Profile.signals
forms.py
from django import forms
from.models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = '__all__'
exclude = ['user']
views.py
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.urls.base import reverse
from .forms import ProfileForm
from .models import Profile
from django.shortcuts import redirect, render,get_object_or_404
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
login_required(login_url="user:login")
def dashboard(request):
return render(request,"dashboard.html")
#login_required(login_url="user:login")
def get_profile(request):
profile = get_object_or_404(Profile,user=request.user)
return render(request,"profile.html",{"profile":profile})

How to return html or json from ViewSets depending on client django rest framework

I've created an APIs endpoints for mobile developers with all of the logic there. Now I need to create a web version of this app. Is it possible to not write the views specifically for this, but use already existing one in APIs?
In API I am using Django Rest Framework ViewSets.
Assume that this is my models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=20)
description = models.CharField(max_length=100, blank=True, null=True)
Then this is going to be my serializers.py:
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id', 'title', 'description')
read_only_fields = ('id',)
viewsets.py:
from rest_framework import viewsets
from .serializers import PostSerializer
from .models import Post
class PostViewSet(viewsets.ModelViewSet):
model = Post
queryset = Post.objects.all()
serializer_class = PostSerializer
urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'posts', viewsets.Post, base_name='post')
urlpatterns = router.urls
I've seen this HTML & Forms in docs, but if I put renderer_classes and template_name it just renders html with the parameters that I want, but I want to have a separate endpoints for just returning json for mobile developers and rendering a user-friendly html for web version. Is it possible to do this and if it is, how do I handle this?
I think you will have to manually create a separate endpoint to handle HTML requests, but you can easily achieve this using Template Renderers

How to find JSON data as ember.js wants?

I am new in django and ember.js. Can you help me about how to find correct JSOn for ember.js
My Code is Here -
In models.py -
from django.db import models
class Confusion(models.Model):
title = models.CharField(max_length=100)
description = models.CharField(max_length=100)
In serializers.py -
from rest_framework import serializers
from confusion.models import Confusion
class ConfusionSerializer(serializers.ModelSerializer):
class Meta:
model = Confusion
fields = ('id', 'title', 'description')
In views.py -
from rest_framework import generics
from confusion.models import Confusion
from confusion.serializers import ConfusionSerializer
class ConfusionList(generics.ListCreateAPIView):
queryset = Confusion.objects.all()
serializer_class = ConfusionSerializer
class ConfusionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Confusion.objects.all()
serializer_class = ConfusionSerializer
Now I am getting Output Like -
[{"id": 1, "title": "Career", "description": "I am confused about my career"}]
But I need:
{"confusion":{"id": 1, "title": "Career", "description": "I am confused about my career"}}
Check out Toran Billups' ember-data-django-rest-adapter. It should do exactly what you need.
Take a look at the README — for basic use, all the extra set-up is on the JavaScript side.
(I basically quote...)
Include the ember-data-django-rest-adapter.js after ember-data.js in your HTML/build system
Add the custom adapter:
App.Store = DS.DjangoRESTStore.extend({
adapter: DS.DjangoRESTAdapter.create()
});
That's it. (In particular is there something you can't get working?)

Django ModelAdmin extra input field not from Model

I'm trying to add an extra input to a admin.ModelAdmin for a model I have so I can record some optional text when another input has changed.
I can't get the custom ModelForm recognised as name 'EquipmentAdmin' is not defined. I've tried several different ways of importing but think I must have missed something obvious. It feels like there's a circular reference between the EquipmentAdmin and EquipmentAdminForm as they both include a reference to each other in the code.
I have created my Django application Flightdeck and have these all in the same folder;
models.py
from django.db import models
class Location(models.Model):
name = models.CharField(max_length=45)
class Equipment(models.Model):
unit_id = models.CharField(max_length=45)
located = models.ForeignKey(Location)
located_from = models.DateField()
class EquipmentLocationHistory(models.Model):
unit = models.ForeignKey(Equipment)
located = models.ForeignKey(Location)
start = models.DateField()
end = models.DateField()
change_reason = models.CharField(max_length=45)
admin.py
from django.contrib import admin
from flightdeck.models import *
from flightdeck.forms import EquipmentAdminForm
class EquipmentAdmin(admin.ModelAdmin):
form = EquipmentAdminForm
def save_model(self, request, obj, form, change):
if 'located' in form.changed_data:
try:
old = Equipment.objects.get(unit_id=obj.unit_id)
except Equipment.DoesNotExist:
old = None
if old:
if 'located' in form.changed_data:
located_history = EquipmentLocationHistory(unit=obj, located=old.located, start=old.located_from, end=obj.located_from)
located_history.save()
obj.save()
forms.py
from django import forms
from django.contrib import admin
class EquipmentAdminForm(forms.ModelForm):
reason = forms.CharField()
class Meta:
model = EquipmentAdmin
I would like to include the reason value when I add the EquipmentLocationHistory but can't test what I have as the EquipmentAdminForm isn't loaded.
EquipmentAdmin is not a model. Your ModelForm needs to reference Equipment
from django import forms
from django.contrib import admin
from flightdeck.models import Equipment
class EquipmentAdminForm(forms.ModelForm):
reason = forms.CharField()
class Meta:
model = Equipment
PS: when you have circular references, there are many ways around the problem. The best way with model imports and django is to use django.db.models.get_model('app', 'model')

Categories

Resources