12👍
Comments per file…
models.py
class Category(models.Model):
category = models.CharField(max_length=100)
The category’s name should be named name
. A field named category
I’d expect to be something like models.ForeignKey("Category")
.
class Article(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
category = models.ManyToManyField(Category)
As Adam pointed out, this should be named categories
. Further, its reverse (the field in Category
that links back to Article
) should be named articles
. So we get:
categories = models.ManyToManyField(Category, related_name="articles")
So now you can get a queryset with all the articles in a category with something like:
get_object_or_404(Category, id=int(cat_id, 10)).articles.all()
views.py
def article_detail(request, article_id=1):
Don’t use a default here. There’s nothing special about the ID 1, and if someone forgets the ID, it should be an error.
def article_create(request):
if request.method == 'POST': # If the form has been submitted...
form = ArticleForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
article = Article.objects.create(
title=form.cleaned_data['title'],
body=form.cleaned_data['body'],
category=form.cleaned_data['category']
)
By using a ModelForm
, this is simplified to:
def article_create(request):
if request.method == 'POST': # If the form has been submitted...
form = ArticleForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
form.save()
return redirect('article_index') # Redirect after POST
else:
form = ArticleForm() # An unbound form
return render(request, 'article_form.html', {'form': form})
forms.py
class ArticleForm(forms.Form):
You really should be using ModelForm
instead (docs here):
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ["title", "body", "category"]
widgets = {
'body': forms.Textarea(),
'category': forms.CheckboxSelectMultiple()
}
On to your questions:
1) in the view ‘article_create’, I’m not sure how to create the category(ies) as part of the Article object. In the shell, I had to create the Article with a call to save(), then add each category after that. Do I need to do something similar here, e.g. create the article then iterate through each category? Example code is appreciated.
IIRC, ModelForm.save()
will take care of this for you.
2) Haven’t coded ‘article_edit’ yet, assuming it will be highly similar to create, but I’m not sure if or how I need to handle the logic for comparing previously selected categories to the current submission. Or, should I just delete all category entries for the article being edited, and re-enter them based on the current submission? That’s probably the easiest. Again, sample code for this would help.
Editing is almost exactly like creating. All you have to do is associate the original object with the form. (Typically, you figure out what the original object is from the URL.) So something like:
def article_edit(request, article_id):
article = get_object_or_404(Article, id=int(article_id, 10))
if request.method == 'POST': # If the form has been submitted...
form = ArticleForm(request.POST, instance=article)
if form.is_valid(): # All validation rules pass
form.save()
return redirect('article_index') # Redirect after POST
else:
form = ArticleForm(instance=article)
return render(request, 'article_form.html', {'form': form})
EDIT: As jheld comments below, you can combine article_create
and article_edit
into one view method:
def article_modify(request, article_id=None):
if article_id is not None:
article = get_object_or_404(Article, id=int(article_id, 10))
else:
article = None
if request.method == 'POST': # If the form has been submitted...
form = ArticleForm(request.POST, instance=article)
if form.is_valid(): # All validation rules pass
form.save()
return redirect('article_index') # Redirect after POST
else:
form = ArticleForm(instance=article)
return render(request, 'article_form.html', {'form': form})
Then the URLs are easy:
url(r"^/article/edit/(?P<article_id>[0-9]+)$", "app.views.article_modify", name="edit"),
url(r"^/article/new$", "app.views.article_modify", name="new"),
2👍
I’d start by renaming category
in the model to categories
, and updating the related code accordingly – the singular naming is just going to be a continuous headache.
At that point, you’re pretty close. In your success branch when submitting an article, assign the categories as a separate statement.
article = Article.objects.create(
title=form.cleaned_data['title'],
body=form.cleaned_data['body']
)
# note changed plural name on the m2m attr & form field
article.categories.add(*form.cleaned_data['categories'])
# alternately
# for cat in form.cleaned_data['categories']:
# article.categories.add(cat)
return redirect('article_index') # Redirect after POST
Oh, and, kudos on avoiding ModelForm
. It’s muuch easier to hook up the form-instance plumbing yourself, this question would be much more complicated with ModelForm
involved.
For the edit view, yes, clear & re-adding is easiest. There are more efficient ways, but nothing that’s worth the complexity until it’s actually a problem. The method call to clear will be article.categories.clear()
, re-adding is same as above.
0👍
You can do like this
for example:
if todo_list_form.is_valid():
todo_list = todo_list_form.save(commit=False)
todo_list.save()
todo_list_form.save_m2m()
- [Django]-How to reload Django models without losing my locals in an interactive session?
- [Django]-Django Private Project Git Repo – Open Source App Repo
- [Django]-Access Django Test Database
- [Django]-Changing django-storages backend from from s3 to cloudfiles and dealing with old files