I have a scenario where a user uploads some data, Django does some processing in pandas and returns a potentially large txt file. I've got this working but I'm unsure about the scalability of the approach and want to know if there's a better way.
Adapted the Outputting to CSV section of the Django doc I have the following:
class MyClass(LoginRequiredMixin,FormView):
template_name = 'myapp/mytemplate.html'
form_class = MyForm
success_url = '/' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = MyForm(request.POST, request.FILES)
#print("filename",files[0].name)
if form.is_valid() :
filename = "my-file.txt"
content = 'any string generated by django'
response = HttpResponse(content, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename={0}'.format(filename)
return response
else:
print("i am invalid")
return self.form_invalid(form)
In practice I will need to output a text file of perhaps 1000 lines, built by looping over numerous dataframes, should I just build an extremely long text string (content), or is there a better way? In pure python I am more used to creating txt file output using:
f = open( 'some_file.txt', 'w+')
f.write("text")
f.write("text")
f.close()
Which seems more intuitive.
As requested by comments, updated to show exactly the code I was trying in Django which was returning an empty text file:
class MyClass(LoginRequiredMixin,FormView):
template_name = 'myapp/mytemplate.html'
form_class = MyForm
success_url = '/' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = MyForm(request.POST, request.FILES)
if form.is_valid() :
f = open( 'some_file.txt', 'w+')
f.write("text")
return FileResponse(f, as_attachment=True, filename='some_file.txt')
It's very simple:
response = HttpResponse(content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename="filename.txt"'
response.write('Hello')
return response
https://docs.djangoproject.com/en/3.2/howto/outputting-csv/
It's same as CSV, just change extension to .txt
Example:
response = HttpResponse(content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename="filename.txt"'
writer = csv.writer(response)
writer.writerow(['Hello'])
Related
I would like to convert to excel contents of a model after passing a filter from the dropdown selection . Here is the view.
def ConvertToExcelView(request):
response = HttpResponse(content_type='text/csv')
writer = csv.writer(response)
writer.writerow([('name'), ('adm'),('form'),('stream') ])
for member in Marks.objects.filter(student__school__name=request.user.school).values_list('student__name', 'student__adm', 'student__klass__name', 'student__stream__name':
writer.writerow(member)
response['Content-Disposition'] = 'attachment; filename="members.csv"'
return response
Is there a way I can incorporate this into the view????
query = request.GET.get('view_classes')
if query:
queryset = (Q(student__klass__name__icontains = query))
return render(request,'students_marks.html',all_mark_results)
Please ask for more clarifications incase I'm not clear with this.
This is fairly simple. Simply include the get request and give a name to the form you intend to get the selection from i.e.
def ConvertToExcelView(request):
details = request.GET.get('s_details') #Assuming the form name is s_details
queryset = (Q(student__klass__name__exact = details))
response = HttpResponse(content_type='text/csv')
writer = csv.writer(response)
writer.writerow([('name'), ('adm'),('form'),('stream') ])
for member in Marks.objects.filter(queryset).distinct().filter(student__school__name=request.user.school).values_list('student__name', 'student__adm', 'student__klass__name', 'student__stream__name':
writer.writerow(member)
response['Content-Disposition'] = 'attachment; filename="members.csv"'
return response
This will work like charm.
Revert if there are explanations you need.
in my Django application, i created a form which permit user to upload csv file.
What i want is when user upload the csv file, the contained data is read and database is filled in with them.
It works but not correctly. data are saved as tuples.
Here's my code
forms.py
class SupplierCSVForm(forms.ModelForm):
class Meta:
model = SuplierCsv
fields = '__all__'
exclude = ('slug',)
views.py
#login_required
def l_supplier(request):
suppliers_list = Supplier.objects.all()
paginator = Paginator(suppliers_list, 3, 2)
page = request.GET.get('page')
suppliers = paginator.get_page(page)
# Supplier csv form
if request.method == 'POST':
form = SupplierCSVForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = request.FILES['csvfile']
with open('f.csv', 'wb') as destination:
for chunk in uploaded_file.chunks():
destination.write(chunk)
destination.close()
#csvfile = io.TextIOWrapper(open('f.csv', 'rb'))
with open('f.csv', 'r') as the_source:
source_reader = csv.reader(sthe_source)
next(source_reader)
for Name, Email, Contact, City, Website, Activity, Cc, slug in source_reader:
new_supplier = Supplier()
new_supplier.name=Name,
new_supplier.email=Email,
new_supplier.contact=Contact,
new_supplier.city=City,
new_supplier.website=Website,
new_supplier.activity=Activity,
new_supplier.cc=Cc,
new_supplier.slug=slug,
new_supplier.save()
return redirect('good:l_good')
else:
form = SupplierCSVForm()
context = {
'suppliers': suppliers,
'form': form,
}
return render(request, 'supplier/l_supplier.html', context)
Remove the commas where you are assigning the new_supplier objects. Python converts your string objects into tuples if there are any trailing commas.
You have unnecessary commas at the end of your lines:
new_supplier.name=Name,
should be
new_supplier.name=Name
Python thinks that you are creating a tuple
i.e. x, == (x,)
I'm attempting to render a PDF and populate the template with specific data from the object that is being viewed.
I'm using xhtml12pdf / pisa. I've successfully rendered a generic (unpopulated) PDF, however when I add logic to populate a specific object's data, my context is being returned however the pdf is no longer being rendered.
views.py
class GeneratePdf(DetailView):
model = Request
template_name='request_response/response.html'
def get(self, request, *args, **kwargs):
context = super(GeneratePdf,self).get(request,*args,**kwargs)
return context
pdf = render_to_pdf('request_response/response.html',context)
if pdf:
response = HttpResponse(pdf,content_type='application/pdf')
filename = "PrivacyRequest_%s.pdf" %("1234")
content = "inline; filename='%s'" %(filename)
response['Content-Disposition'] = content
return response
return HttpResponse("Not Found")
Please show your render_to_pdf function. I assume that it like:
from io import BytesIO
from xhtml2pdf import pisa
from django.template.loader import get_template
def render_to_pdf(template, context):
template = get_template(template)
html = template.render(context)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return None
You can remove context = super(GeneratePdf,self).get(request,*args,**kwargs) and return context.
As shown in the error message, context must be a dict. So change it to dictionary, like:
def get(self, request, *args, **kwargs):
context = {
'example1': 'This is example 1',
'some_foo': 'So many of foo function'
}
pdf = render_to_pdf('request_response/response.html',context)
if pdf:
response = HttpResponse(pdf,content_type='application/pdf')
filename = "PrivacyRequest_%s.pdf" %("1234")
content = "inline; filename='%s'" %(filename)
response['Content-Disposition'] = content
return response
return HttpResponse("Not Found")
FYI: This is another similiar question like yours and it has accepted answer.
I have set up a form and view to upload multiple *.gpx files to my website at once. These files are validated using a clean() method on the form and then once validated passed to a function for processing.
When I upload some invalid files the clean() method catches them and informs the user as expected.
When I upload some valid files the processing function crashes with an error saying the files are empty.
If I comment out the clean() method then the valid files are uploaded fine.
What can be happening to the form during the clean() method than means the files are being blanked?
here is my form:
class UploadGpxForm(forms.Form):
gpx_file = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
here is my view:
class UploadGpxView(FormView):
form_class = UploadGpxForm
template_name = 'dashboard/upload.html' # Replace with your template.
success_url = reverse_lazy('dashboard:index') # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('gpx_file')
if form.is_valid():
for f in files:
SaveGPXtoPostGIS(f)
return self.form_valid(form)
else:
return self.form_invalid(form)
Here is my clean method for the UploadGpxForm:
def clean(self):
file_errors=[]
files = list(self.files.getlist('gpx_file'))
for f in list(files):
#check file has only one full stop in it.
if len(f.name.split('.')) != 2:
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded:'\
'File type is not supported')
, params = { 'file_name': f.name }
, code = 'file_type')
)
#check file doesn't breach the file size listed in settings
if f.content_type in settings.DASHBOARD_UPLOAD_FILE_TYPES:
if f._size > settings.DASHBOARD_UPLOAD_FILE_MAX_SIZE:
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded: File too big.'\
'Please keep filesize under %(setting_size)s.'\
'Current filesize %(file_size)s') ,
params = {
'file_name': f.name,
'setting_size': filesizeformat(
settings.DASHBOARD_UPLOAD_FILE_MAX_SIZE),
'file_size': filesizeformat(f._size)
},
code = 'file_size'
)
)
#check it is one of our allowed file types
else:
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded:'\
'File type is not supported')
, params = { 'file_name' : f.name }
, code = 'file_type'
)
)
#next check the file hasn't been uploaded before
#generate MD5
md5hash = md5()
for chunk in f.chunks():
md5hash.update(chunk)
file_hash = md5hash.hexdigest()
if gpxTrack.objects.filter(file_hash=file_hash).exists():
file_errors.append(ValidationError(
_('%(file_name)s has not been uploaded as a identical file'\
'has already been uploaded previously'),
params = { 'file_name' : f.name },
code = 'file_hash'))
#finally raise errors if there are any
if len(file_errors) > 0:
raise ValidationError(file_errors)
else:
return files
When you read the file content (for calculating md5 hash) you need to move the file object’s position to the beginning (0th byte) using file.seek:
md5hash = md5()
for chunk in f.chunks():
md5hash.update(chunk)
file_hash = md5hash.hexdigest()
f.seek(0) #<-- add this line
I'm trying to upload a picture with rest framework and to test it with the django test client. It almost works, however when I save the file I have this on the top of my file:
--BoUnDaRyStRiNg
Content-Disposition: form-data; name="plop.png"
If I delete those lines, I can perfectly read my picture, but I don't know how to rid of them.
models.py
class GenericUser(User):
avatar = models.ImageField(upload_to=user_directory_path)
views.py
class AvatarView(APIView):
parser_classes = (FileUploadParser,)
def post(self, request):
up_file = request.data['file']
user = get_object_or_404(GenericUser, pk=request.user.id)
user.avatar.save(up_file.name, up_file)
user.save()
return Response(status=204)
tests.py
cl = Client(HTTP_AUTHORIZATION="Token {}".format(token))
with open(MY_FILE, 'rb') as f:
data = f.read()
res = cl.post('/avatar', {"plop.png" : data}, HTTP_CONTENT_DISPOSITION="attachment; filename=plop.png;")
any hints, how I could make this work?
This worked as I expected:
views.py
class AvatarView(APIView):
parser_classes = (MultiPartParser,)
def post(self, request):
up_file = request.data["file"]
user = get_object_or_404(GenericUser, pk=request.user.id)
user.avatar.save(up_file.name, up_file)
user.save()
return Response(status=204)
test.py
cl = Client(HTTP_AUTHORIZATION="Token {}".format(token))
with open(MY_FILE, 'rb') as f:
res = cl.post('/avatar', {"file" : f})
Try this:
res = cl.post('/avatar?filename=plop.png', {"plop.png" : data})
Or more or less from the Django docs:
with open(MY_FILE, 'rb') as f:
cl.post('/avatar', {"filename" : f})