How to get current user in django model? - python

I'm trying to create a dynamic folder to store some csv files for each user.
I have the DemoUser and the Document. It's a 1 to N relationship.
I want to store all documents from a user on its folder, named by the user id.
However I can't figure out how to do it. I can't get the owner name.
here is my code:
models.py:
def get_upload_path(instance, filename):
now = timezone.now().strftime("%Y_%m_%d")
if(instance.owner.id is not None):
return 'graphs/documents/{0}/email/' + now +'_{1}'.format(instance.owner.id, filename)
class Document(models.Model):
docfile = models.FileField(upload_to=get_upload_path, validators=[import_document_validator])
owner = models.ForeignKey(DemoUser)
date_published = models.DateTimeField(_('date published'), default=timezone.now)
views.py:
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(owner=DemoUser.objects.get(id=request.user.id))
newdoc.save()
newdoc.docfile = request.FILES['docfile']
newdoc.save()
The file is uploaded, but the address is:
graphs/documents/{0}/email/2015_10_07_MOCK_DATA_2_qyt9xGe.csv
it doesn't get the instance.owner.id value. I tried to save the Document before adding the docfile to try to set a owner to the object and get the owner.id value but no success. Can you please help me?
Thank you so much!

It should be:
return 'graphs/documents/{0}/email/{1}_{2}'.format(instance.owner.id, now, filename)

A simple method I found to create dynamic directories for each user to store files was to create a new method for a model that assigns a string to the instance that represents the username. This is only good for simple cases like this where you need the current user as a string to help formatting file paths.
#models.py
def get_upload_path(instance, filename):
now = timezone.now().strftime("%Y_%m_%d")
return 'graphs/documents/{0}/email/{1}_{2}'.format(instance.owner, now, filename)
class Document(models.Model):
docfile = models.FileField(upload_to=get_upload_path)
date_published = models.DateTimeField(_('date published'), default=timezone.now)
def keep_owner(self,owner):
self.owner = owner
#views.py
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = form.save(commit=False)
newdoc.keep_owner(request.user)
newdoc.save()

Related

Django: update a model with the current user when an non-model form field is changed

I'm building a page that allows users to edit Task and related Activity records (one task can have many activities), all on the same page. I want to allow the user to "adopt" one or more activities by ticking a box, and have their user record linked to each activity via a ForeignKey. Here are extracts from my code...
models.py
from django.contrib.auth.models import User
class Task(models.Model):
category = models.CharField(max_length=300)
description = models.CharField(max_length=300)
class Activity(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
notes = models.TextField(blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
The activity "owner" is linked to a User from the Django standard user model.
I added an extra field in the form definition for the adopt field - I don't want to add it to the model as I don't need to save it once it's done it's job.
forms.py
class ActivityForm(forms.ModelForm):
adopt = forms.BooleanField(required=False)
class Meta:
model = Activity
fields = '__all__'
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## ? DO SOMETHING HERE ? ##
formset.save()
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
I just can't figure out how to do that - if it was a single form (rather than an InlineFormSet), I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this). Or try save(commit = False) and update the model instance then save() it.
Maybe I have to iterate through the formset in the view code and try one of those options when I find one that had adopt=True?
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
formset = TaskInlineFormSet(request.POST, instance = task)
if formset.adopt:
# If True
formset.user = request.user
formset.save()
I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this).
You should give it a try.
I'm not happy with this solution but it does work. I iterate through the forms and change the object instance if my adopt field is set.
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## HERE'S WHAT I ADDED ##
for form in formset:
if form.cleaned_data['adopt'] is True:
form.instance.owner = request.user
## END OF ADDITIONS ##
formset.save()
## return redirect('manage_task',pk=task.id) # CHANGED THIS BECAUSE I WASN'T RETURNG ERRORS!
if not form.errors and not formset.total_error_count():
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
I wish I could find more in the docs about how the form saving works but I think I'll have to look into the code if I want more detail.

How to populate my Django Model 'updated_by' field with logged in User automatically using a formset?

I am a beginner with Django and I have been enjoying it so far. I figured out how to use model formsets, but I cannot figure out how to make my form automatically use logged in User as the 'updated_by' field.
models.py
class Inventory(models.Model):
item = models.CharField(max_length=50, unique=True)
stock = models.IntegerField()
par = models.IntegerField()
date_updated = models.DateTimeField(auto_now=True)
updated_by = models.ForeignKey(User, on_delete=models.PROTECT)
def __str__(self):
return self.item
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
phone = PhoneField(blank='True', help_text='Contact Phone Number')
def __str__(self):
return f'{self.user.username} Profile'
def save(self):
super().save()
I think the problem lies in your views.py. Try getting request.user before saving the form.
i think you should have made form for Inventory if yes(let InvntoryForm) than in view.py file you have done something like this:-
if request.method == 'POST':
Inven_form=InventoryForm(data=request.POST)
if Inven_form.is_valid():
user=Inven_form.save()
#in between add this
Inven_form.updated_by=request.user.username
user.save()
I would use the 'commit=False' argument which will create a new object and assign it without saving to your database. You can then set the user attribute and call save() with no arguments.
For example, this is how I assigned the user attribute to my blog app.
in views.py
if form.is_valid():
# Create a new entry and assign to new_article.
new_article = form.save(commit=False)
# Set the new article attribute to the current user.
new_article.user = request.user
# Save to database now the object has all the required data.
new_article.save()
Here is the full code for the add_article view if this helps.
#login_required
def add_article(request):
""" Add a new article. """
if request.method != 'POST':
# No data submitted, create a blank form.
form = AddArticleForm()
else:
# POST data submitted, process data.
form = AddArticleForm(request.POST, request.FILES)
if form.is_valid():
new_article = form.save(commit=False)
new_article.author = request.user
new_article.save()
return back_to_blog_page()
context = {'form': form}
return render(request, 'add_article.html', context)

Django uploading multiple media and saving

I've tried to research and understand on the documentation, but unable to solve my problem.
I've a ModelForm which allows me to select and upload multiple files.
However, upon saving, only 1 file is saved to my media root folder despite multiple selection.
My guess is that the filename for all files in getlist are same (since they're uploaded at the same time), it overwrites each other in a way and end up only saving 1 media. Appreciate the assistance from the community, thank you !
forms.py
class FileFieldForm(forms.ModelForm):
stuff_image = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta:
model = Thing
fields = ('title', 'description', 'quantity','stuff_image')
--
model.py
def generate_filename(instance, filename):
ext = filename.split('.')[-1]
return '' + str(int(time())) + '.' + ext
class Thing(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank = True)
quantity = models.IntegerField(blank =True)
creation_date = models.DateTimeField(auto_now_add=True)
stuff_image = models.FileField(upload_to=generate_filename)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
#receiver(post_delete, sender=Thing)
def stuff_post_delete_handler(sender, **kwargs):
Thing = kwargs['instance']
storage, path = Thing.stuff_image.storage, Thing.stuff_image.path
storage.delete(path)
--
view.py
def create_stuff(request):
if request.method == 'POST':
form = FileFieldForm(request.POST, request.FILES)
files = request.FILES.getlist('stuff_image')
if form.is_valid():
for f in files:
form.save()
return redirect('list-stuff')
else:
form = FileFieldForm()
return render(request, 'stuffapp/create_stuff.html', {'form': form})
If you want to have multiple images linked to Thing model, you need to create an Image model with ForeignKey of Thing model. The FileField can only relate to one file at a time.
To upload multiple images/files in django, preferabble use the package django-multiupload, see the (documentation)[https://pypi.python.org/pypi/django-multiupload] or (GitHub Page)[https://github.com/Chive/django-multiupload]. Just follow the instructions and the examples. Works flawlessly.

Django: Save user uploads in seperate folders

I want individual users to be able to upload their files into a single folder (so each user has their own root folder, where they can upload their own files), but I am not sure of the best way to go about implementing this.
I originally planned to use the users email as the their folder name, and all their uploads would be saved in this folder. However, from what I have gathered the only way of retrieving this information is through the request function, and I cannot manage to get an instance of request into the models.py file, and therefore cannot add it to my 'upload_to' directory.
Any other ideas of how to separate users files, or how to get an instance of request in the models file would be greatly appreciated!!
Here is my current model:
def user_directory_path(instance, filename):
return 'user_{0}/{1}'.format(instance.user.id, filename)
class UploadModel(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to=user_directory_path)
And it's associated error:
Exception Value: UploadModel has no user.
I don't recommend you to use user email or any other information that can be updated as folder name because you won't change folder name each time he changes his email or his username. So, use user id that is unique and unchangeable.
Here is a complete example from Django documentation, to access instance information in your models to build path with user id :
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
In this case, it use the user id in the folder name. Of course, your can replaceFileField with ImageField.
More information in django docs : FileFields.upload_to
You could maybe separate the folder by their username? You can create a function that would create a folder using the users username like so:
def get_user_image_folder(instance, filename):
return "%s/%s" %(instance.user.username, filename)
and in your model you could easily use the upload_to and add the function that you just created to it:
class Images(models.Model):
user = models.ForeignKey(User)
image = models.ImageField(upload_to=get_user_image_folder,
verbose_name='Image', )
You don't have to use request in Models, you use instance instead.
To get this to work I implemented the solution from the docs as suggested by Louis Barranqueiro, whereby the models looks like:
# models.py
def user_directory_path(instance, filename):
return 'user_{0}/{1}'.format(instance.user.id, filename)
class Document(models.Model):
file = models.FileField(upload_to=user_directory_path)
uploaded_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='documents')
But crucially, I also changed my DocumentUploadView class to include a save step, so that the document saves with the user attribute (note also the initial commit=False save step, which is crucial):
# views.py
class DocumentUploadView(View):
def get(self, request):
documents_list = Document.objects.all()
return render(self.request, 'file_upload.html', {'documents': documents_list})
def post(self, request):
form = DocumentForm(self.request.POST, self.request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.user = request.user
document.save()
data = {'is_valid': True, 'name': document.file.name, 'url': document.file.url}
else:
data = {'is_valid': False}
return JsonResponse(data)
Finally my forms.py looks like this:
# forms.py
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('file',)
For anyone else in the future that stumble across this, I got access to the current user's ID by adding the user object to the model in the view.
views.py
from .models import Document
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.owner = request.user
newdoc.save()
Then, in models.py you can retrieve the ID from owner created previously in view.
def user_directory_path(instance, filename):
return 'Users/user_{0}/{1}'.format(instance.owner.id, filename)
class Document(models.Model):
docfile = models.FileField(upload_to=user_directory_path)
In your views.py you must pass the instance argument like this:
def post(self, request, *args, **kwargs):
if 'uploadFile' in request.POST:
f = UploadFileForm(request.POST, request.FILES, instance=request.user.uploadmodel)
if f.is_valid():
f.save()
return HttpResponseRedirect('/sucess_url')
And your forms.py
from django import forms
from .models import UploadForm
class UploadFileForm(forms.ModelForm):
class Meta:
model = UploadForm
fields = ('file',)
I also faced the same problem. Here is how I solved it. We have to create the user referencing in the view.
Below is the code for models.py
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class Document(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
docfile = models.FileField(upload_to=user_directory_path)
date = models.DateTimeField(auto_now_add=True, blank=True)
Also update the code in views.py as below:
def upload(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'],user=request.user)
newdoc.save()
latest_documents = Document.objects.all().order_by('-id')[0]
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('list'))
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the list page
documents = Document.objects.filter(user=request.user)
The most important one is in the line,
newdoc = Document(docfile=request.FILES['docfile'],user=request.user)
Somehow I could not get the model instance to read the user. This approach is working for me.

Django claas Admin auto-populate user field

I am trying to populate the field 'owner' in the my NoteForm. I read in documentation that I need to use the Admin for that.But i still get this error : note_note.owner_id may not be NULL. Need help.
Code:
forms.py:
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title','body')
models.py:
class Note(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
cr_date = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, blank=False)
class NoteAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
form.owner = request.user
form.save()
views.py:
def create(request):
if request.POST:
form = NoteForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponceRedirect('/notes/all')
else:
form = NoteForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('create_note.html', args)
i also tried to write the class NoteAdmin in admin.py , just in case. Same error.What i am doing wrong? I am just following the documentation.
You are trying to save Note without owner field and the error said it. Try like:
if form.is_valid():
obj = form.save(commit=False)
obj.owner = request.user
obj.save()
The other way is to set null=True for the owner field if you can write data without owners.

Categories

Resources