models.py:
class FWVersion(models.Model):
bin_file = models.FileField(upload_to='fW_media/bin/')
date_created = models.DateTimeField(default=timezone.now)
name = models.CharField(max_length=64)
serializers.py:
class UploadFWSerializer(serializers.ModelSerializer):
class Meta:
model = FWVersion
exclude = ('date_created',)
class GetFWSerializer(serializers.ModelSerializer):
class Meta:
model = FWVersion
fields = ('name', 'bin_file',
)
views.py:
class GetFWView(viewsets.ModelViewSet):
queryset = FWVersion.objects.all()
serializer_class = serializers.GetFWSerializer
def get_object(self):
lastest_fw = FWVersion.objects.first()
return lastest_fw
class UploadFWView(mixins.CreateModelMixin,
generics.GenericAPIView):
serializer_class = serializers.UploadFWSerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
I am using django-rest-framework to create APIs for uploading and retrieving the latest version of the .bin file. The APIs are working and I am able to upload a .bin file on the server. But when downloading the file again, the server returns the first uploaded file(the oldest one)even though the name, url are of the latest uploaded file. Sometimes rarely however, it returns the latest file. I have checked the server files, they are being uploaded properly. Is this because of some caching by Django or am I missing something? Please help.
Your code pretty clearly calls first() on the queryset in GetFWView, so I'm not sure why this behaviour surprises you. If you want to always return the latest one, then you should do that:
def get_object(self):
lastest_fw = FWVersion.objects.latest('date_created')
As an aside, you shouldn't be using a ViewSet for that view; it only supports one operation, which is GET, so you should just use a RetrieveAPIView.
def get_attachment_path(instance, filename):
ext = os.path.splitext(filename)[1]
d = os.path.dirname(filename)
current_time = datetime.now().strftime('%Y%m%d-%H%M%S-%f')[:-3]
filename = os.path.join(d, current_time + ext)
return 'fW_media/bin/{}'.format(filename)
class FWVersion(models.Model):
bin_file = models.FileField(upload_to=get_attachment_path)
name = models.CharField(max_length=64)
create_time = models.DateTimeField(auto_now_add=True)
class GetFWView(viewsets.ModelViewSet):
queryset = FWVersion.objects.all()
serializer_class = GetFWSerializer
def create(self, request, *args, **kwargs):
serializer = UploadFWSerializer(data=request.data)
if serializer.is_valid():
instance = serializer.create(serializer.validated_data)
return Response('upload success')
else:
return Response(serializer.errors)
#list_route(methods=['GET'])
def get_latest(self, request):
lastest_fw = FWVersion.objects.latest('create_time')
serializer = self.get_serializer(lastest_fw)
return Response(serializer.data)
Related
I am trying to upload a csv file and then using it to populate a table in the database (creating multiple objects).
serializers.py:
def instantiate_batch_objects(data_list, user):
return [
WorkData(
work=db_obj['work'],
recordTime=db_obj['recordTime'],
user=user
) for db_obj in data_list
]
class FileUploadSerializer(serializers.ModelSerializer):
filedata = serializers.FileField(write_only=True)
class Meta:
model = WorkData
fields = ['user', 'filedata']
def create(self, validated_data):
file = validated_data.pop('filedata')
data_list = csv_file_parser(file)
batch = instantiate_batch_objects(data_list, validated_data['user'])
work_data_objects = WorkData.objects.bulk_create(batch)
# print(work_data_objects[0])
return work_data_objects
views.py:
class FileUploadView(generics.CreateAPIView):
queryset = WorkData.objects.all()
permission_classes = [IsAuthenticated]
serializer_class = FileUploadSerializer
# I guess, this is not need for my case.
def get_serializer(self, *args, **kwargs):
print(kwargs.get('data'))
if isinstance(kwargs.get('data', {}), list):
kwargs['many'] = True
return super().get_serializer(*args, **kwargs)
models.py
class WorkData(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='work_data',
)
work = models.IntegerField(blank=False, null=False)
recordTime = models.DateTimeField(blank=False, null=True)
When I upload the file and post it I get this error:
Got AttributeError when attempting to get a value for field user on serializer FileUploadSerializer. The serializer field might be named incorrectly and not match any attribute or key on the list instance. Original exception text was: 'list' object has no attribute 'user'.
But I can see table is populated successfully in the database. What should I return from create method of FileUploadSerializer?
OK, after trying an example myself I was able to reproduce the errors, I have a better understanding of why this is happing now.
First, let's put the implementation of create() on the view class here
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
The original error of Got AttributeError when attempting to get a value for field user... etc happened because the create() in the FileUploadView is returning serializer.data which is expecting fields user and filedata but create() on FileUploadSerializer is returning a list of objects so you can see now why this is happening.
You can solve this by overriding create() on FileUploadView and serialize the returned serializer data with a WorkDataSerializer that you will create
For ex:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
workData = WorkDataSerializer(data=serializer.data)
return Response(workData.data, status=status.HTTP_201_CREATED, headers=headers)
OR, you can do it on serializer level - which I prefer -
For example:
class FileUploadSerializer(serializers.ModelSerializer):
filedata = serializers.FileField(write_only=True)
created_objects_from_file = serializers.SerializerMethodField()
def get_created_objects_from_file(self, obj):
file = self.validated_data.pop('filedata')
data_list = csv_file_parser(file)
batch = instantiate_batch_objects(data_list, self.validated_data['user'])
work_data_objects = WorkData.objects.bulk_create(batch)
return WorkDataSerializer(work_data_objects, many = True).data
class Meta:
model = WorkData
fields = ['user', 'filedata']
class WorkDataSerializer(serializers.Serializer):
# fields of WorkData model you want to return
This should work with no problems, note that SerializerMethodField is read_only by default
see https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
I have a models.py as the following and I'm trying to make multiple upload files in one request.
and the other fields in the model i put the values in the back-end so, what i need exactly how to to send array of data (files) in one request and handle the files and create record for every single files separate?
I also read a lot and see a lot of answers, but I felt the solution depends on the case maybe because I didn't got it will
please any one can help me ?
models.py
file_name = models.FileField(upload_to='docs/', null=True, blank=True)
created_by = models.ForeignKey(User, related_name='file_created_by', blank=True, null=True,on_delete=models.DO_NOTHING)
created_date = models.DateTimeField(auto_now_add=True)
serializers.py
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = '__all__'
def __init__(self, *args, **kwargs):
many = kwargs.pop('many', True)
user = kwargs['context']['request'].user
super(FileSerializer, self).__init__(many=many, *args, **kwargs)
def create(self, validated_data):
validated_data['status'] = 'in_progress'
self.context["file_name"] = self.context['request'].FILES.get("file_name")
obj = File.objects.create(**validated_data)
return obj
views.py
class FileCreateAPIView(CreateAPIView):
queryset = File.objects.all()
serializer_class = FileSerializer
permission_classes = [IsOwnerOrReadOnly]
def get_queryset(self):
return File.objects.all()
def perform_create(self, serializer):
serializer.save(created_by=self.request.user, updated_by=self.request.user)
Update file_name model field's default serializer field to serializers.ListField and update the create method in serializer to loop over the list to create multiple objects.
Example:
class FileSerializer(serializers.ModelSerializer):
file_name = serializers.ListField(
child=serializers.FileField(
max_length=100000, # length of the file name
allow_empty_file=False,
use_url=False
),
write_only=True
)
class Meta:
model = File
fields = ('created_by', 'file_name', )
def create(self, validated_data):
files = validated_data.pop("file_name")
obj = None
# can also use `bulk_create`, if too many files
for file in files:
obj = File.objects.create(file_name=file, **validated_data)
return obj
NOTE: The serializer will now not output any file_name keys, if you use it for list usecase. For that, you can add another field fname or something as a serializers.SerializerMethodField and return the value of the file name.
Example:
class TestSerializer(serializers.ModelSerializer):
test_field = serializers.SerializerMethodField()
def get_test_field(self, obj):
# since your field would be a file, so you can access `name` attribute
return obj.test_field.name
fields = ('test_field', ...rest of the fields...)
I am beginner to Django and currently, I can construct model like this.
models.py
class Car(models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=5, decimal_places=2)
photo = models.ImageField(upload_to='cars')
serializers.py
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('id','name','price', 'photo')
views.py
class CarView(APIView):
permission_classes = ()
def get(self, request):
car = Car.objects.all()
serializer = CarSerializer(car)
return Response(serializer.data)
For photo, it doesn't show full URL. How can I show full URL?
Django is not providing an absolute URL to the image stored in a models.ImageField (at least if you don't include the domain name in the MEDIA_URL; including the domain is not recommended, except of you are hosting your media files on a different server (e.g. aws)).
However, you can modify your serializer to return the absolute URL of your photo by using a custom serializers.SerializerMethodField. In this case, your serializer needs to be changed as follows:
class CarSerializer(serializers.ModelSerializer):
photo_url = serializers.SerializerMethodField()
class Meta:
model = Car
fields = ('id','name','price', 'photo_url')
def get_photo_url(self, car):
request = self.context.get('request')
photo_url = car.photo.url
return request.build_absolute_uri(photo_url)
Also make sure that you have set Django's MEDIA_ROOTand MEDIA_URL parameters and that you can access a photo via your browser http://localhost:8000/path/to/your/image.jpg.
As piling pointed out, you need to add the request while initialising the serializer in your views.py:
def my_view(request):
…
car_serializer = CarSerializer(car, context={"request": request})
car_serializer.data
For future visitors, there is no need to add another field to the serializer if the view method already returns a serialized object. The only thing required is to add the context since it is needed to generate hyperlinks, as stated in the drf documentation
#list_route()
def my_view(self, request):
qs = Object.objects.all()
return Response(MySerializer(qs, many=True, context={'request': request}).data)
Serializer class
class CarSerializer(serializers.ModelSerializer):
photo_url = serializers.ImageField(max_length=None, use_url=True, allow_null=True, required=False)
class Meta:
model = Car
fields = ('id','name','price', 'photo_url')
View
class CarView(APIView):
def get(self, request, *args, **kwargs):
queryset = Car.objects.all()
serializer = CarSerializer(queryset, many=True, context={"request":request})
return Response(serializer.data, status=status.HTTP_200_OK)
It's better to use this code, due to the above code doesn't check the image is null able or not.
class CarSerializer(serializers.ModelSerializer):
photo_url = serializers.SerializerMethodField()
class Meta:
model = Car
fields = ('id','name','price', 'photo_url')
def get_photo_url(self, car):
request = self.context.get('request')
if photo and hasattr(photo, 'url'):
photo_url = car.photo.url
return request.build_absolute_uri(photo_url)
else:
return None
serializers.py
class BannerSerializer(serializers.ModelSerializer):
image = serializers.SerializerMethodField()
def get_image(self, obj):
return self.context['request'].build_absolute_uri( obj.image.url)
views.py
banner = Banner.objects.all()
banner_data = BannerSerializer(banner,many=True, context={'request': request})
data = banner_data.data
return Response({"data":data})
I read the implement of the Serializer, and find the simplest way is to extends ImageField:
from django.db import models
class ImageField(models.ImageField):
def value_to_string(self, obj): # obj is Model instance, in this case, obj is 'Class'
return obj.fig.url # not return self.url
class Class(models.Model):
name = models.CharField(max_length=50)
intro = models.CharField(max_length=200)
# fig = models.ImageField(upload_to="classes")
fig = ImageField(upload_to="classes")
def __str__(self):
return repr(self,"name")
So, my issue is that I have a detailview, which displays a specific post from my database. I then, used get_context_data to then grab db values from a different model; however, it outputs something strange in my template.
What can I change in the template, in order for it to list every correct db value from that other model?
models.py
class Projects(models.Model):
user = models.ForeignKey(User)
slug = models.SlugField()
project_title = models.CharField(max_length=30)
project_shortdesc = models.CharField(max_length=248)
project_desc = models.TextField()
def save(self):
super(Projects, self).save()
date = datetime.date.today()
self.slug = '%i%i%i%s' % (
date.year, date.month, date.day, slugify(self.project_title)
)
super(Projects, self).save()
class ProjectsToDo(models.Model):
project_tododate = models.DateField()
project_tododesc = models.TextField(max_length = 500)
project_id = models.ManyToManyField(Projects)
views.py
class ProjectDetail(generic.DetailView):
model = Projects
context_object_name = 'indprojects'
template_name = 'projectpage.html'
def get_context_data(self, *args, **kwargs):
context = super(ProjectDetail, self).get_context_data(*args, **kwargs)
context['todolist'] = ProjectsToDo.objects.order_by('project_tododate')
context['todoform'] = ProjectToDoForm()
context['form'] = ProjectForm(instance=Projects.objects.get(slug=self.kwargs['slug']))
return context
def get_queryset(self):
return Projects.objects.filter(user=self.request.user)
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(ProjectDetail, self).dispatch(request, *args, **kwargs)
template
{{todolist}}
This from the template, outputs:
[<ProjectsToDo: ProjectsToDo object>, <ProjectsToDo: ProjectsToDo object>]
I've tried {{todolist.project_tododesc}}, and both outputted no data. I'm not really sure how to go about fixing this, any help would be appreciated.
I think the problem is that you did not define a __str__() method if you are using Python 3 (or __unicode__() in Python 2.x) in class ProjectsToDo.
When you try to print a model instance, what happens is Django will look at __str__() to decide what to display.
See here for more about __str__()
How do I go by showing only user files that they upload (not all) I'm using python 2.7 and django 1.7 with django allauth. Below is my model. If anyone can point me to the right direction thank you.
models.py
def hashed_uploads_dirs(instance, filename):
return os.path.join(instance.md5, filename)
class File(models.Model):
f = models.FileField(upload_to='.')
md5 = models.CharField(max_length=32)
created_at = models.DateTimeField(auto_now_add=True)
def was_published_recently(self):
return self.created_at >= timezone.now() - datetime.timedelta(days=1)
was_published_recently.admin_order_field = 'created_at'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
def __unicode__(self):
return self.f.name
#models.permalink
def get_absolute_url(self):
return ('file-add', )
def save(self, *args, **kwargs):
self.slug = self.file.name
super(File, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
"""delete -- Remove to leave file."""
self.file.delete(False)
super(File, self).delete(*args, **kwargs)
You need to modify your file class to store a foreign key to a user. It should look like
...
from django.contrib.auth.models import User
class File(models.Model):
f = models.FileField(upload_to='.')
md5 = models.CharField(max_length=32)
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, related_name='uploaded_files')
...
When you upload the file, you'll also have to set the user before saving the file. Then when you have an instance of User you can get all the uploaded files with user.uploaded_files.
Create a new field in File:user = models.ForeignKey(User)
Then you can do
file.user = request.user
file.save()
in the function that handles the file upload