import copy import magic from django import forms from .models import Expense, Revenue class CleanInvoiceForm(forms.ModelForm): """ We have to define this form explicitly because we want our ImageField to accept PDF files as well as images, and we cannot change the clean_* methods with an autogenerated form from inside views.py """ invoice = forms.FileField() def clean_invoice(self): # get the uploaded file from cleaned_data uploaded_file = self.cleaned_data["invoice"] # is this a valid image? try: # create an ImageField instance im = forms.ImageField() # now check if the file is a valid image im.to_python(uploaded_file) except forms.ValidationError: # file is not a valid image, so check if it's a pdf # do a deep copy so we dont mess with the file object we might be passing on testfile = copy.deepcopy(uploaded_file) # read the uploaded file into memory (the webserver limits uploads to a reasonable max size so this should be safe) mimetype = magic.from_buffer(testfile.open().read(), mime=True) if mimetype != "application/pdf": raise forms.ValidationError("Only images and PDF files allowed") # this is either a valid image, or has mimetype application/pdf, all good return uploaded_file class ExpenseCreateForm(CleanInvoiceForm): class Meta: model = Expense fields = [ "description", "amount", "invoice_date", "invoice", "paid_by_bornhack", "responsible_team", ] class ExpenseUpdateForm(forms.ModelForm): class Meta: model = Expense fields = [ "description", "amount", "invoice_date", "paid_by_bornhack", "responsible_team", ] class RevenueCreateForm(CleanInvoiceForm): class Meta: model = Revenue fields = [ "description", "amount", "invoice_date", "invoice", "responsible_team", ] class RevenueUpdateForm(forms.ModelForm): class Meta: model = Revenue fields = ["description", "amount", "invoice_date", "responsible_team"]