I have a django model that looks like this
class Foo(models.Model):
...
article = models.FileField(upload_to='articles', blank=True, default=None, null=True)
...
when I try to delete an object bar of Foo model using bar.delete() where any file has not been uploaded in the article field, I get the following error.
[Error2] Is a directory
How can I delete such objects?
EDIT
The code for the deletion looks like this:
HTML
<div class="form-group">
<input type="radio" name="delete" value="yes" /> Delete
<input type="radio" name="delete" value="no" checked="checked" /> Don't Delete
</div>
DJANGO
def del_foo(request,foo_id):
context_dict = {'msg':'Unable to delete'}
try:
bar = models.Foo.objects.get(id=foo_id)
_delete = True if request.POST.get("delete")=="yes" else False
if _delete:
bar.delete()
return HttpResponseRedirect('/home')
except Exception as e: print str(e)
return render(request, 'edit.html', context_dict)
You haven't included the relevant code for the delete operation, but it sounds like you calling os.remove(path). Since the filename is blank the path to be removed is being constructed as something like os.path.join(dir, '') resulting in just dir. Calling os.remove(dir) results in the errro you indicated. You'll need to avoid this in the delete operation.
def delete(self):
if self.article:
# delete operation
# rest of delete, maybe the default parent delete
super(Foo, self).delete(*args, **kwargs)
There are certainly more complicated things you can do but that should point you in a hopefully helpful direction.
in Django 1.9
i created
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Foo(models.Model):
article = models.FileField(upload_to='articles', blank=True, default=None, null=True)
then i created two objects
Foo.objects.create(pk=1)
Foo.objects.create(pk=2)
then i tried to delete it
Foo.objects.get(pk=1).delete()
(1, {u'Foo': 1})
its worked like charm!
Related
I am creating a single page blog site how can i get the post id to the comments form , I created django forms with necessary field but the problem is ,I have to select the post id from a drop down menu manually while commenting, for that I passed post object as an input value of a form to views.py file but django needs instance to save in database what should I do now
note :I am not using post_detail
models.py
class comments(models.Model):
name=models.CharField(max_length=255)
content=models.TextField()
post=models.ForeignKey(blog,related_name="comments",on_delete=models.CASCADE)
#blog is the model to which comment is related
date=models.DateTimeField(auto_now_add=True)
forms.py
class commentform(ModelForm):
class Meta:
model=comments
fields=('name','content','post')
widgets={
'name' : forms.TextInput(attrs={'class':'form-control','placeholder':'type your name here'}),
'content' : forms.Textarea(attrs={'class':'form-control'}),
'post' : forms.Select(attrs={'class':'form-control'})
}
Html
<form method='POST' action="comment_action" class='form-group'>
{%csrf_token%}
{{form.name}}
{{form.content}}
<input type="text" id="objid" name="objid" value="{{objs.id}}" hidden>
<button class="btn btn-primary btn-sm shadow-none" type="submit">Post comment</button>
views.py
def comment_action(request):
name=request.POST.get('name')
content=request.POST.get('content')
objid=request.POST.get('objid')
to_db=comments.objects.create(name=name,content=content,post=objid)
print(name,content,objid)
return redirect('/homepage')
return render(request, 'index.html')
ERROR :
Exception Type: ValueError
Exception Value:
Cannot assign "'48'": "comments.post" must be a "blog" instance.
-->48 is my blog id
i know that database will only accept instance post field because that was a foreign key
my question is how to pass it ?
You assign it with post_id:
def comment_action(request):
name = request.POST.get('name')
content = request.POST.get('content')
objid = request.POST.get('objid')
to_db = comments.objects.create(name=name, content=content, post_id=objid)
print(name,content,objid)
return redirect('/homepage')
Note: It is better to use a Form [Django-doc]
than to perform manual validation and cleaning of the data. A Form will not
only simplify rendering a form in HTML, but it also makes it more convenient
to validate the input, and clean the data to a more convenient type.
Note: normally a Django model is given a singular name, so Comment instead of comments.
I want to change my Foreign Key to Many To Many field to let the user select multiple categories in a dropdown list.
This is what I already have. After I change Foreign Key to Many To Many I'm getting milion errors, I have to get rid of on_delete=models.CASCADE which is a core of my app. What can I do? Which way should I take? Maybe add another model? I'm so confused, especially when I am a Django newbie. Thank you for your help!
MODELS
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return f'{self.name}'
class Expense(models.Model):
class Meta:
ordering = ('date', '-pk')
category = models.ForeignKey(Category, null=True,blank=True, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
amount = models.DecimalField(max_digits=8,decimal_places=2)
date = models.DateField(default=datetime.date.today,db_index=True)
def __str__(self):
return f'{self.date} {self.name} {self.amount}'
The clue of the application is to let the user create a category e.g "PC". Then add some expenses like "GPU", "CPU" etc... and let the user link it to the "PC" category. And when the user wants to delete certain categories, all the expenses linked to it, gonna be deleted too. And this is the thing I have already did. BUT NOW I want to let the user search the main table of expenses by multiple categories. And here comes my problem, I don't have a clue how to do it and keep the whole application in one piece with all the functionalities.
SCREENSHOTS:
Categories View with just added PC category
Expense Add View
I don't think there is a simple answer to your question, but here are some resources that might help. First, I don't think you should change your models. From the way you described your application, I think a foreign key model with on_delete=CASCADE is good. The basic idea here is that you need to change your list view function so that it performs a query of your database. Also modify your template.html to include a search bar.
https://github.com/csev/dj4e-samples/tree/master/well
https://www.dj4e.com/lessons/dj4e_ads4
Modify Your List View To Allow The Searching
This is an example of a list view that allows you to search for a single term, and returns anything in the database that matches from any field. This isn't what you want to do exactly, but if you can get this working then you can modify the search conditions for your specific application. What is going on in the code below is that instead of return every item in my Ad table in my SQL database, I filter it based on the search. Then, I pass "ad_list" to the template view. Since I already filtered ad_list based on the search, in the template view it will only list the items that match. This is based on the DJ4E course, and you can watch the video there to get an idea of how he implements the search bar better.
from ads.models import Ad
from django.views import View
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.http import HttpResponse
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.contrib.humanize.templatetags.humanize import naturaltime
from ads.utils import dump_queries
from django.db.models import Q
class AdListView(ListView):
# By convention:
template_name = "ads/ad_list.html"
def get(self, request) :
strval = request.GET.get("search", False)
if strval :
# Simple title-only search
# objects = Ad.objects.filter(title__contains=strval).select_related().order_by('-updated_at')[:10]
# Multi-field search
query = Q(title__contains=strval)
query.add(Q(text__contains=strval), Q.OR)
objects = Ad.objects.filter(query).select_related().order_by('-updated_at')[:10]
else :
# try both versions with > 4 posts and watch the queries that happen
objects = Ad.objects.all().order_by('-updated_at')[:10]
# objects = Ad.objects.select_related().all().order_by('-updated_at')[:10]
# Augment the post_list
for obj in objects:
obj.natural_updated = naturaltime(obj.updated_at)
ctx = {'ad_list' : objects, 'search': strval}
retval = render(request, self.template_name, ctx)
dump_queries()
return retval;
Modify Your Template.html to include a search bar
<form>
<input type="text" placeholder="Search.." name="search"
{% if search %} value="{{ search }}" {% endif %}
>
<button type="submit"><i class="fa fa-search"></i></button>
<i class="fa fa-undo"></i>
</form>
PS, I think you can answer your own question better when you figure it out, so help others and post it!
I'm working on a simple project that requires a candidate to enter a description of his/herself. Since this requires lots of text, I use models.TextField() in models.py file and <textarea> tag in html.
In models.py
class Candidate(Person, models.Model):
#Other fields
description = models.TextField()
def __str__(self):
return self.name
In Html
<!---some stuff up-->
<label for="candidate_description">Describe yourself</label>
<textarea id="candidate_description" placeholder="Description..."></textarea>
<!---some stuff below-->
views.py file
def regCandidate(request):
if request.method == 'POST':
candidate = Candidate(
description=request.POST.get('candidate_description'),
)
candidate.save()
return render(request, 'Election/registerCandidate.html', {})
When I run the server and try to submit I get a IntegerityError. I have did some done some research and
found out the error is occurs when django receives a null value when it is required. I'm a beginner at django and I'm using custom forms. Can anyone help explain why django is receiving a None and how to fix it.
Fields in form are passed on name and value basis. I am guessing that since you don't have a name attribute in Textarea, therefore that field is not passed to server.
Try adding a name attribute to the textarea field:
<textarea id="candidate_description" name="candidate_description" placeholder="Description..."></textarea>
I am trying to implement a tagging process for profiles so you can add your hobbies for example.
I have chosen django-taggit as it seemed quite simple and does what I need it to, plus don't really know how to do it myself from scratch.
I have managed to make it work to some extent but I am having issues with 3 things:
Not really sure what's the best way to control the form field for these tags as I generate the form automatically with widget adjustments in meta function of the form, but it might work fine after resolving the below two issues.
When there is no data for the field hobbies (tags) the field gets populated with a single tag of value "[]" as per below image.
When I add a tag of "music" and submit the form after I reload the page I get this "[]" as per image. I assumed this will be dealt with by the library, but I cannot see another similar scenario online.
When I try adding another tag of "games" and save and reload, the below happens. The initial value gets wrapped again.
My model is:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
hobbies = TaggableManager()
My form is:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['hobbies',]
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args,**kwargs)
self.fields['hobbies'].widget = forms.TextInput()
self.fields['hobbies'].widget.attrs['data-role'] = "tagsinput"
self.fields['hobbies'].widget.attrs['class'] = "form-control"
self.fields['hobbies'].required = False
My view function is:
if request.method == 'POST':
user_profile = UserProfile.objects.get(user=request.user)
form = UserProfileForm(request.POST, instance=user_profile)
print(form)
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
print("Form valid")
form.save_m2m()
Using:
<script src="/static/js/tagsinput.js"></script>
<link rel="stylesheet" href="{% static 'css/tagsinput.css' %}" />
I had this exact same problem.
One solution is to apply the data-role="tagsinput" AFTER you turn a list of tags into a comma-separated string for the form.
Here is that solution:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, **kwargs):
self.fields['tags'].widget.attrs['value'] = ", ".join(list(self.instance.tags.names()))
self.fields['tags'].widget.attrs['data-role'] = "tagsinput"
Output:
As you can see, there's a problem with quotes appearing around tags that are multi-word. It also causes new tags with quotes to be saved to the database.
If double-quotes didn't appear around multi-word phrases, this would be the most elegant solution. If someone solves this in the future, drop a note!
My template is this:
<div class="m-3 p-3 border">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit">Save Form</button>
</form>
</div>
I know I can use a template tag to strip the extra quotes from the tag field itself, but then I'd have to go through and create all the form fields manually just to set the tags template tag.
For the time being, my solution is to simply use Javascript and just modify the Meta widgets section of the form.
FINAL ANSWER (for now):
forms.py
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'tags': forms.TextInput(attrs={
"data-role": "tagsinput",
})
}
custom.js - put this script on the page that loads the form.
document.addEventListener("DOMContentLoaded", function(event) {
let tags_input = document.querySelector('#id_tags');
let tags_input_value = tags_input.value;
let new_value = [...tags_input_value.matchAll(/<Tag:\s*([\w\s]+)>/g)].map(([, m]) => m).join(', ')
tags_input.setAttribute('value', new_value);
}
So all we're doing is modifying the front-end presentation, and leaving all the backend internal forms functionality untouched.
So after quite a few (hundreds) of tests, I finally narrowed down where the issue was and tried to go around it with successful result.
It seems the data got amended into tag objects through tagsinput library I was using. Only when the "data-role" was specified as "tagsinput" in the forms.py the data would already come to html side as those objects and be shown incorrectly. So instead I wanted to keep the data clean and only apply data-role='tagsinput' in the end for visual aspect, which I did using:
var hobbiesTags = document.getElementById("id_hobbies");
if(hobbiesTags){
var att = document.createAttribute("data-role");
att.value = "tagsinput";
hobbiesTags.setAttributeNode(att);
};
And that resulted in the below. Maybe there are better ways to do this, I'm not sure, but it's a pretty clean solution. Share your alternatives.
I'm working on an app that will let users build their own databases, and one of the fields they can create is a photo field. I'm using a form model, and while it captures the rest of the data, it won't try to grab the files.
I have file uploads working elsewhere on the site, and if I upload the file manually in the admin panel it shows up just fine, but the form itself won't grab the file.
In my template, I have:
<form action="/orgs/{{org.id}}/db/{{db.unique_id}}/rows/add/" method="post" enctype="multipart/form-data">
{% csrf_token %}
...
{% elif column.field_type = 23 %}
{{form.photofield}}
{% endif %}
</form>
In my models:
from django.db import models
def get_upload_file_name(instance, filename):
return settings.UPLOAD_FILE_PATTERN % (str(time()).replace('.','_'), filename)
class DbField(models.Model):
...
photofield = models.FileField(upload_to=get_upload_file_name, null=True, blank=True)
in my forms:
from django import forms
from byodb.models import DbField
class DbFieldForm(forms.ModelForm):
class Meta:
model = DbField
fields = ('photofield',)
widgets = {'photofield': forms.FileInput()}
and in my views:
def org_database_add_row(request, organization_id=1, db_id=1):
if request.POST:
if request.POST.get(str(column.unique_id)):
if column.field_type == 23:
form = DbFieldForm(request.POST, request.FILES)
if form.is_valid():
f = form.save(commit=False)
...
f.save()
sorry for any confusion about column/row stuff, it's just data to place the field. If you need more detail then I can add more, but on the surface it looks like everything SHOULD work...
ALTERNATIVELY, I have been trying to get it to work another way, avoiding using the form altogether so that the file field will be named the same as my column.
In that instance, in my template it reads:
<input type="field" name="{{column.unique_id}}" id="photofield" />
and in my views it reads:
elif column.field_type == 23:
DbField.objects.create(row=row, column=column, unique_id=column.unique_id, creator=request.user, last_editor=request.user, field_type=column.field_type, photofield=request.FILES[str(column.unique_id)])
However it's the same issue, it will create the field just fine, but it won't try to grab the file. I'm not sure why it's only failing here, as this works everywhere else on the site.
My bad, I figured out the problem.
In my views, where I had:
if request.POST:
if request.POST.get(str(column.unique_id)):
if column.field_type == 23:
DbField.objects.create(row=row ... photofield=request.FILES[str(column.unique_id)])
It was failing because request.POST.get(str(column.unique_id)) was empty since it was FILES and not POST.
I rewrote the view to accomodate:
if request.POST.get(str(column.unique_id)):
...
else:
if request.FILES[str(column.unique_id)]:
if column.field_type == 23:
DbField.objects.create(row=row ... photofield=request.FILES[str(column.unique_id)])
else:
DbField.objects.create(row=row ... all other fields left blank)
This way if the request.POST for the field comes up empty, it checks to see if there's a file attached and the correct field type, if not then it just creates the empty field.