Django video field contains url or file - django

How to make only one field of these two fields?
is it possible?
class MyModel(models.Model):
video_file = models.FileField(blank=True)
video = models.URLField(blank=True)
def clean(self, *args, **kwargs):
if not self.video_file and not self.video: # This will check for None or Empty
raise ValidationError({'video_file': 'Even one of field1 or field2 should have a value.'})
elif self.video_file and self.video: # This will check for None or Empty
raise ValidationError({'video_file': 'Even one of field1 or field2 should have a value.'})
if self.video == '':
self.video = self.video_file.url
super(MyModel, self).save(*args, **kwargs)```
**UPDATED**
I think this is the solution, my bad.

Having only one option
The easiest solution might be to not accept 2 different types, and only support either Image upload or Image URL. I'd suggest Image upload only if you're going to implement this solution.
However, if having those 2 options is a requirement you can take a look at the solutions I've listed below.
Checking at a controller level (Simple solution)
One solution is to check if both fields are populated at the controller level, or View in django jargon. If both are populated you can throw some error and handle it from there.
Changing the model and handling at service level (Recommended)
The above solution might work, but that wouldn't be the ideal solution for the long run.
I'd recommend you to change your model to only have a FileField, then in service layer you can directly upload if user uploads a file, however if user passes a URL you can download the image and save it.
You can also make the DB field a UrlField, and if user uploads a file, you can upload it to some external storage bucket like s3 or cloudinary and save the URL in your database.
As for the constraint, you can apply the constraint as mentioned above in solution 2 of adding the constraint in controller or some other way using django magic.

Related

Adding file upload widget for BinaryField to Django Admin

We need to store a few smallish files to the database (yes, I'm well aware of the counterarguments, but setting up e.g. FileField to work in several environments seems very tedious for a couple of files, and having files on the database will also solve backup requirements).
However, I was surprised to find out that even though BinaryField can be set editable, Django Admin does not create a file upload widget for it.
The only functionality we need for the BinaryField is the possibility to upload a file and replace the existing file. Other than that, the Django Admin fulfills all our requirements.
How can we do this modification to Django Admin?
You will want to create a custom Widget specifically for BinaryField which has to read the file contents before putting them into the database.
class BinaryFileInput(forms.ClearableFileInput):
def is_initial(self, value):
"""
Return whether value is considered to be initial value.
"""
return bool(value)
def format_value(self, value):
"""Format the size of the value in the db.
We can't render it's name or url, but we'd like to give some information
as to wether this file is not empty/corrupt.
"""
if self.is_initial(value):
return f'{len(value)} bytes'
def value_from_datadict(self, data, files, name):
"""Return the file contents so they can be put in the db."""
upload = super().value_from_datadict(data, files, name)
if upload:
return upload.read()
And then you need to use it in admin in the following way:
class MyModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.BinaryField: {'widget': BinaryFileInput()},
}
fields = ('name', 'your_binary_file')
Note:
BinaryField doesn't have a url or a file name so you will not be able to check what's in the db
After uploading the file you will be able to see just the byte size of the value stored in the db
You might want to extend the widget to be able to download the file
by reading it's contents

Returning related fields of a model instance

I am creating an app with a rest API that should return values for instances of objects based on the url given. Right now I have the API working using ModelViewSets of my objects for the API.
For example I have three objects, user, transactions, and goals.
As it stands I can go to /mysite/api/users and return a list of all users
I can also go to /mysite/api/users/1 to return just the user with the id '1'.
I can do something similar with transactions and goals.
What I'm looking to do is go to url /mysite/api/users/1/transaction/1/goal
to find the goal associated with the transaction for that user.
I've been scouring tutorials and am not sure what the right question is to ask in order to find something useful to learn how to do this. What is the correct way to go about setting up my rest api like this?
If I understand correctly, you want to create nested ressources.
If you are using Viewsets, then the ExtendedRouter class of the drf-extensions package will allow you to achieve this.
Drf-extensions documentation about this feature: https://chibisov.github.io/drf-extensions/docs/#nested-routes
There is also this module, who also offer the same features.
You can either use url params or query params to solve your issue. I will explain the URL params solution here,
serializers.py
#Write a Goal Serializer
urls.py
#change the URL according to your environment
url(r'^users/(?P<uid>[0-9]+)/transaction/(?P<tid>[0-9]+)/goal/$', GoalViewSet.as_view({'get': 'user_transaction_goal',}), name='user-transaction-goal'),
views.py
class GoalViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
queryset = Goal.objects.all()
def user_transaction_goal(self, request, uid, tid):
#assuming user is FK in transaction and transaction is a FK in goal
#modify the filter rule according to your model design
goals = Goal.objects.filter(transaction=tid, transaction__user=uid)
serializer = GoalSerializer(goals, many=False)
return Response(serializer.data)
As #clement mentioned you can also use plugins to handle this situation.

Django how to access a uploaded file in views?

In my views.py file, there are some functions like follows:
def upload_file(request):
"""
upload a file and store it into the database,
and the file will be predicted in another view immediately.
"""
def predict(request):
"""
make prediction of the file uploaded in the 'upload_file' function.
"""
How to access the file which uploaded in upload_file function in the predict function? Here is my thought:
1. read the last row in the database, but this sounds a little silly.
2. use a cache system and retrieve the file from cache?
Is there any useful solution for this problem? please give me any hints or other resources.
Thanks for anyone's help.
As it is described here, you can acces the storage url as Follows :
Class MyModel(models.Model):
photo = models.ImageField()
model = MyModel.objects.get(pk=1) # for instance
model.photo.url
>>> 'https://media.example.com/mymodels/example_image.jpg'

django model: check relation before saving the object

I have django model consists of two class annualReport and annualReportAttachment
The relation between the two models is oneToMany. In the admin form I need to validation that the user has uploaded at least one file so I use the following clean method in the annualReport class
def clean(self):
attachments = annualReportAttachment.objects.filter(annualReport=self)
if len(attachments) == 0:
raise ValidationError("You should upload at least one file")
The problem is that the attached files is not saved yet so the attachments variable is empty and the form always raise that error.
How could I check that the user has uploaded at least one file?
You'll need to make sure that at least one form in your inline model gets saved. To do this, I would recommend leveraging the RequireOneFormSet class from https://code.google.com/p/wadofstuff/wiki/WadOfStuffDjangoForms

Form/ModelForm instances between requests

I want write a custom form field (and possibly widget too) and I'm not sure about how the form instances are shared between requests. For example, if I render a form with data from a model instance, is that instance still available when I am validating data? If so, does that mean that there is another database hit to look up the model again between requests?
Similarly, if I write a custom field that takes in a list of data to display in its __init__ method, will that list of data be available to validate against when the user POSTs the data?
It would be really helpful if someone could point me to parts of the django source where this occurs. I've been looking at the models.py, forms.py, fields.py and widgets.py from django.forms, but I'm still not 100% sure how it all works out.
Eventually, what I want to do is have a field that works something like this (the key part is the last line):
class CustomField(ChoiceField):
def __init__(self, data_dict, **kwargs):
super(CustomField, self).__init__(**kwargs)
self.data_dict = data_dict
self.choices = data_dict.keys()
def validate(self, value):
if value not in self.data_dict:
raise ValidationError("Invalid choice")
else:
return self.data_dict[value]
Will that data_dict be available on the next request? If I create a custom forms.Form and initialize it with the data_dict, will that be available on the next request? (e.g. with a factory method or something...).
Side note: I'm doing this because I want to (eventually) use something like Bootstrap's typeahead and I'd like to pass it "pretty values" which I then convert server-side (basically, like how option values in a select can have a different submitted value). I've done this with client-side javascript in the past, but it would be nice to consolidate it all into a form field.
There's nothing magical about forms. Like everything else in Django (or just about any web framework), objects don't persist between requests, and need to be reinstantiated each time. This happens in the normal view pattern for form handling: you instantiate it once for a POST, and a separate time for a GET. If you have data associated with the form, it would need to be passed in each time.