I like clarifying that I'm new to Django.
Versions:
Django 1.5
Python 2.7
PostgreSQL 9.3
I have a webapp that uploads a file to AWS S3, this works currently. I would like to name the file the name of the contentid field that gets auto assigned a V4 UUID on upload. I'll try and pick out the relevant information and post it here. I'm not sure where I would gather this information and declare it for the name.
View
def upload_content(request):
if request.method == 'POST':
form = ContentForm(request.POST, request.FILES)
if form.is_valid():
new_content = Content(name=request.POST['name'],accountid=request.user.id,public=False,url=request.POST['name'],uploaddate=datetime.now(),viewcount='0',file = request.FILES['file'])
new_content.save()
return HttpResponseRedirect('/Console/Content/')
Model
class Content(models.Model):
name = models.CharField(max_length=128)
accountid = models.IntegerField(max_length=34)
url = models.CharField(max_length=200)
uploaddate = models.DateTimeField('date published')
viewcount = models.IntegerField(max_length=34)
public = models.BooleanField(max_length=1)
contentid = UUIDField(unique=True,editable=False)
file = models.FileField(upload_to='content')
#classmethod
def get_content_list(cls, account):
cursor = connection.cursor()
cursor.execute('SELECT name, contentid, public, uploaddate, id FROM webapp_content WHERE accountid=%s ORDER BY uploaddate', [account])
ret = cursor.fetchall()
return ret
So I have somewhat of an answer, but I'm still left with an issue
model:
def generate_new_filename(instance, filename):
ext = filename.split('.')[-1]
filename = '{}.{}'.format(uuid.uuid4().hex, ext)
return (filename)
class Content(models.Model):
file = models.FileField(upload_to=generate_new_filename)
view:
still the same as above, how do I write to my sql the newly generated filename on save in place of request.FILES['file']
The outcome:
file get's written the correct variable, url does not
Related
Currently working on a django social media application where next to posting every post to the feed, the information of each upload should create a pdf document, containing caption, image, created_at and image_id.
I´ve put the canvas into the upload functions, so that both will be created on the same click. So far, i get a pdf (can't get the attributes from the post into the pdf tho) and the post is uploaded just fine.
How do I get the posted data into the pdf?
And how do save that pdf to a folder within the project instead of starting an automatic download? The user should not be able to notice the pdf converting. It is just for back office - but very necessary due to the social media website being a part of a big installation. So how do I get these pdfs?
Here is the code:
views.py
def upload(request):
if request.method == 'POST':
#user = request.user.username
image = request.FILES.get('image_upload')
caption = request.POST['caption']
new_post = Post.objects.create( image=image, caption=caption)
new_post.save()
#create pdf
buffer = io.BytesIO()
p = canvas.Canvas(buffer)
p.drawString(100, 100, "Hello world.")
p = request.FILES.get('post_pdf')
p.drawText('caption')
p.drawImage('image')
p.showPage()
p.save()
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename='hello.pdf')
return redirect('/')
else:
return redirect('/')
models.py
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
image = models.ImageField(upload_to='post_images')
caption = models.TextField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
number_of_likes = models.IntegerField(default=0)
number_of_dislikes = models.IntegerField(default=0)
#interaction_count = models.IntegerField(default=0)
#engagement_count = models.IntegerField(null=True)#number_of_dislikes + number_of_likes
def __str__(self):
return self.caption
To add the value of Post.caption into the pdf, use the value of new_post.caption, change this:
p.drawText('caption')
for this:
p.drawText(new_post.caption)
Same for other fields.
This is not as hard as it seems,
so Let's see if you are successful in creating a pdf and now you have to store it in background instead of downloading.
file_name = request.FILES["file_name"]. #suppose file_name is file name
file_name = default_storage.save(rf"{basePath}/media/whatsapp/file_name.pdf", file_name) #{basePath}/media/whatsapp/ is the path name where we want it to be stored
I'm trying to make dynamic upload path to FileField model. So when user uploads a file, Django stores it to my computer /media/(username)/(path_to_a_file)/(filename).
E.g. /media/Michael/Homeworks/Math/Week_1/questions.pdf or /media/Ernie/Fishing/Atlantic_ocean/Good_fishing_spots.txt
VIEWS
#login_required
def add_file(request, **kwargs):
if request.method == 'POST':
form = AddFile(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.parent = Directory.objects.get(directory_path=str(kwargs['directory_path']))
post.file_path = str(kwargs['directory_path'])
post.file_content = request.FILES['file_content'] <-- need to pass dynamic file_path here
post.save()
return redirect('/home/' + str(post.author))
MODELS
class File(models.Model):
parent = models.ForeignKey(Directory, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
file_name = models.CharField(max_length=100)
file_path = models.CharField(max_length=900)
file_content = models.FileField(upload_to='e.g. /username/PATH/PATH/..../')
FORMS
class AddFile(forms.ModelForm):
class Meta:
model = File
fields = ['file_name', 'file_content']
What I have found was this, but after trial and error I have not found the way to do it. So the "upload/..." would be post.file_path, which is dynamic.
def get_upload_to(instance, filename):
return 'upload/%d/%s' % (instance.profile, filename)
class Upload(models.Model):
file = models.FileField(upload_to=get_upload_to)
profile = models.ForeignKey(Profile, blank=True, null=True)
You can use some thing like this(i used it in my project):
import os
def get_upload_path(instance, filename):
return os.path.join(
"user_%d" % instance.owner.id, "car_%s" % instance.slug, filename)
Now:
photo = models.ImageField(upload_to=get_upload_path)
Since the file_path is an attribute on the File model, can you not build the full path something like this:
import os
def create_path(instance, filename):
return os.path.join(
instance.author.username,
instance.file_path,
filename
)
And then reference it from your File model:
class File(models.Model):
...
file_content = models.FileField(upload_to=create_path)
Link to docs
The other answers work flawlessly; however, I want to point out the line in the source code that allows such functionality. You can view the function, generate_filename, here, in Django's source code.
The lines that make the magic happen:
if callable(self.upload_to):
filename = self.upload_to(instance, filename)
When you pass a callable to the upload_to parameter, Django will call the callable to generate the path. Note that Django expects your callable to handle two arguments:
instance
the model that contains the FileField/ImageField
filename
the name of the uploaded file, including the extension (.png, .pdf, ...)
Also note that Python does not force your callable's arguments to be exactly 'instance' and 'filename' because Django passes them as positional parameters. For example, I prefer to rename them:
def get_file_path(obj, fname):
return os.path.join(
'products',
obj.slug,
fname,
)
And then use it like so:
image = models.ImageField(upload_to=get_file_path)
views.py
def contact_list(request):
importform = ImportExcelForm()
if request.method == 'POST':
importform = ImportExcelForm(request.POST, request.FILES)
if importform.is_valid():
input_excel = request.FILES['input_excel']
book = xlrd.open_workbook(file_contents=input_excel.read())
excel_parser= ExcelParser()
success, log = excel_parser.read_excel(request.FILES['input_excel'] )
return redirect('/member/incident-types/')
else:
importform = ImportExcelForm()
return render(request, 'incident/contact_list.html',
{
'about_menu': True,
'ImportExcelForm':importform,
})
forms.py
class ImportExcelForm(Form):
input_excel = forms.FileField()
user = forms.ModelChoiceField(queryset=Contacts.objects.all())
def save(self):
records = xls.reader(self.cleaned_data["input_excel"])
for line in records:
input_data = Data()
input_data.place = self.cleaned_data["user"]
input_data.name = line[1]
input_data.number = line[2]
input_data.save()
models.py
class Contacts(models.Model):
user = models.ForeignKey(User, null=True)
name = models.CharField('Name', max_length=100)
number = models.IntegerField()
This is to import the xls file,read and write the name and contact number in xls file to database field.I am not getting any error in this but it is not parsing the data in database.
I'm not sure what you're expecting to happen here. Your form save method (assuming it's indented correctly, which it isn't) iterates over the contents of self.cleaned_data["input_excel"], which is a filename, not a file. But in any case, your view does not call the save method at any point. What it does do is use a completely different set of Excel parsing functions, reads them into a pair of local variables, then does nothing at all with the contents of those variables which go out of scope when the function returns.
I suggest you think about exactly what your logic flow should be, write that down, then write the code to match it.
like django's upload_to
def upload_to(instance, filename):
filename = time.strftime('%Y%m%d%H%M%S')
ym = time.strftime('%Y%m')
return 'uploads/%s/%s.jpg' % (ym,filename)
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
photo = models.ImageField(u"Image (Upload)",upload_to=upload_to)
file saved 'uploads/%s/%s.jpg'
but change for
photo = FileBrowseField("Image", max_length=200, directory="uploads/", extensions=[".jpg"], blank=True, null=True)
How to rename a file before uploading it to a folder
like django's upload_to
In filebrowser/sites.py you can create a hook for this when uploading / handling uploads:
def _upload_file(self, request):
"""
Upload file to the server.
"""
if request.method == "POST":
folder = request.GET.get('folder', '')
if len(request.FILES) == 0:
return HttpResponseBadRequest('Invalid request! No files included.')
if len(request.FILES) > 1:
return HttpResponseBadRequest('Invalid request! Multiple files included.')
filedata = list(request.FILES.values())[0]
fb_uploadurl_re = re.compile(r'^.*(%s)' % reverse("filebrowser:fb_upload", current_app=self.name))
folder = fb_uploadurl_re.sub('', folder)
path = os.path.join(self.directory, folder)
# we convert the filename before uploading in order
# to check for existing files/folders
file_name = convert_filename(filedata.name)
filedata.name = file_name
file_path = os.path.join(path, file_name
....
You can modify file_path here to whatever you like, or modify the file name.
For those of you that just want to ensure that files are not overwritten, you can set the FILEBROWSER_OVERWRITE_EXISTING flag in your settings.py as such:
FILEBROWSER_OVERWRITE_EXISTING = False
This will ensure that when you edit your files you give them a unique name, and it also ensures new uploads get their filename converted to something unique using filebrowsers convert_filename method defined in filebrowser/utils.py
More on filebrowser settings here. Hope this helps :)
For all these many years of this question and similar questions on other sites, I have not found a ready-made solution, but only hints of it. It's time to post the solution:
signals.py:
from filebrowser.signals import filebrowser_pre_upload
import os
import time
def pre_upload_callback(sender, **kwargs):
fullname = kwargs['file'].name
filename, extension = os.path.splitext(fullname)
new_filename = time.strftime('%Y%m%d%H%M%S')
kwargs['file'].name = new_filename + extension
filebrowser_pre_upload.connect(pre_upload_callback)
I hope I helped someone
I've tried to import a csv file into a database by tweaking the modelform inside the admin doing this:
models.py:
class Data(models.Model):
place = models.ForeignKey(Places)
time = models.DateTimeField()
data_1 = models.DecimalField(max_digits=3, decimal_places=1)
data_2 = models.DecimalField(max_digits=3, decimal_places=1)
data_3 = models.DecimalField(max_digits=4, decimal_places=1)
Forms.py:
import csv
class DataImport(ModelForm):
file_to_import = forms.FileField()
class Meta:
model = Data
fields = ("file_to_import", "place")
def save(self, commit=False, *args, **kwargs):
form_input = DataImport()
self.place = self.cleaned_data['place']
file_csv = request.FILES['file_to_import']
datafile = open(file_csv, 'rb')
records = csv.reader(datafile)
for line in records:
self.time = line[1]
self.data_1 = line[2]
self.data_2 = line[3]
self.data_3 = line[4]
form_input.save()
datafile.close()
Admin.py:
class DataAdmin(admin.ModelAdmin):
list_display = ("place", "time")
form = DataImport
admin.site.register(Data, DataAdmin)
But i'm stuck trying to import the file i put in "file_to_import" field. Getting AttributeError in forms.py : 'function' object has no attribute 'FILES'.
What i'm doing wrong?
After a long search i found an answer: Create a view inside the admin using a standard form
Form:
class DataInput(forms.Form):
file = forms.FileField()
place = forms.ModelChoiceField(queryset=Place.objects.all())
def save(self):
records = csv.reader(self.cleaned_data["file"])
for line in records:
input_data = Data()
input_data.place = self.cleaned_data["place"]
input_data.time = datetime.strptime(line[1], "%m/%d/%y %H:%M:%S")
input_data.data_1 = line[2]
input_data.data_2 = line[3]
input_data.data_3 = line[4]
input_data.save()
The view:
#staff_member_required
def import(request):
if request.method == "POST":
form = DataInput(request.POST, request.FILES)
if form.is_valid():
form.save()
success = True
context = {"form": form, "success": success}
return render_to_response("imported.html", context,
context_instance=RequestContext(request))
else:
form = DataInput()
context = {"form": form}
return render_to_response("imported.html", context,
context_instance=RequestContext(request))
The rest is part of this post:
http://web.archive.org/web/20100605043304/http://www.beardygeek.com/2010/03/adding-views-to-the-django-admin/
Take a look at django-admin-import, it does more or less exactly what you want -- you can upload a XLS (not a CSV, but that should not matter) and lets you assign columns to model fields. Default values are also supported.
https://pypi.org/project/django-admin-import/
Additionally, it does not take away the possibility to modify individual records by hand because you don't have to replace the default model form used in the administration.
In the save() method, you don't have any access to the request object - you can see that it's not passed in. Normally you would expect to have a NameError there, but I suspect that you've got a function elsewhere in the file called request().
At the point of saving, all the relevant data should be in cleaned_data: so you should be able to do
file_csv = self.cleaned_data['file_to_import']
At that point you'll have another problem, which is when you get to open - you can't do that, as file_to_import is not a file on the server filesystem, it's an in-memory file that has been streamed from the client. You should be able to pass file_csv directly to csv.reader.