I am trying to create a delete function for my Workout model.
This is the model:
class Workout(models.Model):
workoutID = models.AutoField(primary_key=True)
name = models.CharField(max_length=40)
created_by = models.ForeignKey(User)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def delete(self):
return reverse("delete_workout", kwargs = {'workout_id': self.workoutID})
Next I have the view:
def delete_workout(request, workout_id):
workout = get_object_or_404(Workout, workoutID = workout_id)
print(workout)
if request.user != workout.created_by:
return HttpResponse('Not ur workout')
else:
workout.delete()
return HttpResponseRedirect('/')
This is the url:
url(r'^(?P<workout_id>\d+)/delete/$', views.delete_workout, name='delete_workout'),
And finally the html:
<a href='{{ instance.delete }}'>
<button>Delete Workout</button>
</a>
I'm not getting any errors in the console, which is why I don't know what is going wrong.
You are overriding delete method of the class just for getting the delete url. You will get the url by url function in the template like {% url delete_workout instance.workoutID %}. So remove the delete function from the model change your html href url. Leave the view and url as the same. No issues there
class should be
class Workout(models.Model):
workoutID = models.AutoField(primary_key=True)
name = models.CharField(max_length=40)
created_by = models.ForeignKey(User)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
And your html should be
<a href='{% url delete_workout instance.workoutID %}'>
<button>Delete Workout</button>
</a>
NOTE: django model itself adds id for each table, so you dont have to specify it as you did workoutID = models.AutoField(primary_key=True).
By default each model will have a id field just like id = models.AutoField(primary_key=True)
If you consider removing the workoutID then the model becomes
class Workout(models.Model):
name = models.CharField(max_length=40)
created_by = models.ForeignKey(User)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
and the html will be
<a href='{% url delete_workout instance.id %}'>
<button>Delete Workout</button>
</a>
Django has all the tools for you under the hood. Don't reinvent the wheel. You can refactor and simplify your code.
First remove the method delete in Workout.
Second, replace your function-based-view with a class-based-view:
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
from django.http import Http404
from .models import Workout
class WorkoutDeleteView(DeleteView):
model = Workout
success_url = reverse_lazy('delete_workout')
def get_object(self):
obj = super().get_object()
if obj.created_by != self.request.user:
raise Http404
return obj
A workout can be deleted only by its author. In success_url you specify the target where the user should be redirected after deleting.
Just adapt slightly your urls.py (pay attention to the emphasised part):
url(r'^(?P<pk>\d+)/delete/$', views.WorkoutDeleteView.as_view(), name='delete_workout'),
EDIT:
You can name your views as you please, however it would be better to follow already well established conventions. Thus the names for the class based views should be workout-list, workout-detail, workout-create, workout-update and workout-delete.
Related
Hello i am new to django and i am trying to create a page where we can add and participate in various events.
This is the model i created for my database
model.py
class Venue(models.Model):
name = models.CharField('Venue Name', max_length=120)
address = models.CharField(max_length=300)
zip_code = models.CharField('Zip Code', max_length=6)
phone = models.CharField('Contact Number', max_length=25, blank=True)
web = models.URLField('Website Address', blank=True)
email = models.EmailField('Email', blank=True)
owner = models.IntegerField('Venue Owner', blank=False, default=1)
venue_image = models.ImageField(null=True, blank=True, upload_to="images/")
class Event(models.Model):
name = models.CharField('Event Name', max_length=120)
event_date = models.DateTimeField('Event Date')
venue = models.ForeignKey(Venue, blank=True,null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(User,blank=True,null=True,on_delete=models.SET_NULL)
description = models.TextField(blank=True, )
attendees = models.ManyToManyField(User,blank=True, related_name='attendees')
here i am trying to make a link by clicking that link user participate to that Event
but i am not getting how to put the user data in the above attendees field
view function
def attendees(request):
Event.attendees.add(request.user)
return redirect('list-events')
error : AttributeError at /participate/
'ManyToManyDescriptor' object has no attribute 'add'
link
Participate
url.py
path('participate/', attendees, name='participate')
You need to specify for which event the user will be added, so the view should look like:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST
#require_POST
#login_required
def attendees(request, pk):
event = get_object_or_404(Event, pk=pk)
event.attendees.add(request.user)
return redirect('list-events')
participating should be done through a POST request since it alters entities. A GET request should only be used to retrieve data, not update it.
and the urls.py thus should contain a URL parameter for the primary key:
path('participate/<int:pk>/', attendees, name='participate')
Finally the template should thus make a POST request to the path with the primary key of the event:
<form method="post" action="{% url 'participate' pk=event.pk %}">
<button type="submit">Participate</button>
<form>
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: One can use the #require_POST decorator [Django-doc]
to restrict the view to only be accessible for a POST request.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Here is the easiest way of doing it. As I can understand Venue model is saved by the admin(i.e you). If not then you have to save the Venue first.
Now the key point is, in relationship fields, you have to pass the whole inheriting model object.
You can do something like this:
#in login view
def login(request):
#your code
request.session['userID']=userID
#your code
now you can use the session variable (i.e userID) in any view or template
#participate view
from mymodels import Event,Venue
def participate(request):
#your GET or POST parameters here
user=user_model.objects.get(id=request.session['userID'])
venue=Venue.objects.get(id=venue_id) #OR filter it by any other field like name
E1=Event()
E1.attendees= user
E1.venue= venue
E1.manager= user
E1.name= name_from_request_parameters
E1.description= description_from_request_parameters
E1.event_date= event_date_from_request_parameters
E1.save()
Tip: use default current date in datefield like
event_date = models.DateTimeField(default=datetime.now())
I am trying to update a many to many field button when a user clicks a button
The Payment model is to allow users to submit their receipts to be reimbursed. Each Payment has a "many to many" field named status. Currently, there are only two statuses a payment can have "complete" or "incomplete". When a user submits a payment the default is incomplete, and when the company issues a check to reimburse the user, the company needs to be able to click a button that will update the payment to 'complete'.
To get a better idea of what this looks like, the HTML page looks like this.
The Page
My models.py
from django.db import models
from django.contrib.auth.models import User
import uuid
from django.urls import reverse
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return f'{self.name}'
class Status(models.Model):
type = models.CharField(max_length=100)
def __str__(self):
return f'{self.type}'
class Payment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
amount = models.DecimalField(max_digits=14, decimal_places=2)
receipt_img = models.FileField(default='default.jpg', upload_to='receipt_pics')
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
type = models.ManyToManyField(Category)
time_posted = models.DateTimeField(auto_now_add=True)
description = models.TextField(null=True)
status = models.ManyToManyField(Status)
def __str__(self):
return f'{self.id} Contribution'
def get_absolute_url(self):
return reverse('contrib-home')
The button that sends the request in payment_detail.html
<a href="{% url 'update-status' operation='complete' pk=object.id %}">
<button type="button" class="btn btn-primary">Complete</button>
</a>
The urls.py
path(r'^connect/(?P<operation>.+)/(?P<pk>\d+)/$', views.update_status, name='update-status')
The views.py
class PaymentDetailView(DetailView):
model = Payment
template_name = 'financial/payment_detail.html'
def update_status(request, operation, pk):
payment = Payment.objects.get(id=pk)
if operation == 'complete':
print("Many to many field print")
payment.status.first().type = 'complete'
payment.save()
return redirect('payment-detail', pk=pk)
return redirect('payment-detail', pk=pk)
I have tried multiple solutions from other posts but I keep getting many errors like Cannot edit many to many fields. Any insight or help would be very helpful because there doesn't seem to be much on the internet for this.
You need to save the Status object not the payment object.
def update_status(request, operation, pk):
payment = Payment.objects.get(id=pk)
if operation == 'complete':
print("Many to many field print")
status = payment.status.first()
status.type = 'complete'
status.save()
return redirect('payment-detail', pk=pk)
return redirect('payment-detail', pk=pk)
Also if you want to update all status objects related to payment this would also work payment.status.all().update(type='complete')
I have a model.py file that has classes Author and Article. Article has a foreign key referencing Author. I have created a view,blogCreate, using a form ,ArticleForm, in my forms.py file. Since author in class Article is a foreign key, it means that author will be chosen from the Author queryset. This means that the select tag will automatically used by the form, instead I want to use the <input type="text" > tag so that I can create an instance of Author using the input and not select from the queryset.
forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('title', 'content', 'aurthor')
widgets = {
'title': forms.TextInput(attrs={
'class': 'title'}),
'content': forms.Textarea(attrs={
'class': 'text_input',
'name': 'article_content'}),
# Changed to TextInput so it can use <input type="text" >
'aurthor': forms.TextInput(attrs={
'class': 'text_input',
'name': 'aurthor_name'})
}
models.py
from django.db import models
from ckeditor.fields import RichTextField
class Aurthor(models.Model):
name = models.CharField("Author Name", max_length=100)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField("Title", max_length=100)
content = RichTextField(blank=True, null=True)
pub_date = models.DateTimeField("Publish Date", auto_now_add = True)
aurthor = models.ForeignKey(Aurthor, on_delete=models.CASCADE)
def __str__(self):
return self.title
views.py
from .models import Article, Aurthor
from django.views.generic import CreateView
from .forms import ArticleForm
class blogCreate(CreateView):
model = Article
form_class = ArticleForm
template_name = 'BlogHandler/blog.html'
blog.html
<form action="" method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Post</button>
</form>
I finally found a way to do exactly what I wanted, I don't if how efficient it is but it works. Let me know if there is a better way.
forms.py
from django import forms
from .models import Article, Author
class ArticleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs)
self.fields['author'] = forms.CharField(max_length=100, required=True)# author is required
class Meta:
model = Article
fields = ('title', 'content', )
In the question, I stated that I'd changed the widget for author to TextInput so that I could enter text not an instance of Author.This didn't work out the way I wanted, so instead I removed author from fields and made a custom field author that is not a field in my model. This way I still get the text input to create my Author instance.
models.py
class Article(models.Model):
title = models.CharField("Title", max_length=100, null=False)
content = RichTextField(blank=True, null=False)
pub_date = models.DateTimeField("Publish Date", auto_now_add = True)
author = models.ForeignKey(Author, on_delete=models.CASCADE, null=True)
I made author nullable in my models.py file but it is okay because I made the custom auhtor field in forms.py required so that all Articles made using the site have an Author. I was getting some error before this change
views.py
class articleCreate(CreateView):
model = Article
form_class = ArticleForm
def form_valid(self, form):
rt = super().form_valid(form)
article = form.save(commit=False)
author_name = self.request.POST['author'].title()
author, created = Author.objects.get_or_create(name=author_name)
article.author = author
article.save()
return rt
Here I first pause the save so that I can create an Author using the text input from the custom field author, which is simple text, if the Author instance already exists it gets else it creates it. Then I save and I'm done.
UPDATE #2
Status: Still not solved
Updated: Thurs. Dec. 18, 11:30 a.m.
I'm currently using FullArticle.objects.order_by('?').first() to get a random article from my database, but it's not working. There is probably something missing from my models, view or url.py that's missing.
models.py
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class FullArticleQuerySet(models.QuerySet):
def published(self):
return self.filter(publish=True)
class FullArticle(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=150)
slug = models.SlugField(max_length=200, unique=True)
pubDate = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
category = models.CharField(max_length=150)
heroImage = models.CharField(max_length=250, blank=True)
relatedImage = models.CharField(max_length=250, blank=True)
body = models.TextField()
publish = models.BooleanField(default=True)
gameRank = models.CharField(max_length=150, blank=True, null=True)
objects = FullArticleQuerySet.as_manager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("FullArticle_detailed", kwargs={"slug": self.slug})
class Meta:
verbose_name = "Blog entry"
verbose_name_plural = "Blog Entries"
ordering = ["-pubDate"]
views.py
from django.views import generic
from . import models
from .models import FullArticle
# Create your views here.
class BlogIndex(generic.ListView):
queryset = models.FullArticle.objects.published()
template_name = "list.html"
randomArticle = FullArticle.objects.order_by('?').first()
class BlogDetail(generic.DetailView):
model = models.FullArticle
template_name = "detailed.html"
urls.py
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns(
'',
url(r'^$', views.BlogIndex.as_view(), name="list"),
url(r'^(?P<slug>\S+)', views.BlogDetail.as_view(), name="detailed"),
)
Section in list.html that I want to be random
<div class="mainContent clearfix">
<div class="wrapper">
<h1>Top 10 Video Games</h1>
{% for article in object_list|slice:":1" %}
<p class="date">{{article.pubDate|date:"l, F j, Y" }}</p> | <p class="author">{{article.author}}</p>
<img src="{{article.heroImage}}" alt="" class="mediumImage">
<p class="caption">{{article.body|truncatewords:"80"}}</p>
{% endfor %}
I assume that FullArticle.objects.order_by('?')[0] will give me a
random item from my class of FullArticle. But, let's say that out of
my model, I only want data associated with the specific parts of the
article: title, author, heroImage and body. How would I go about doing
that?
To get specific fields of an object, use values or values_list. The first will return dictionaries, the second tuples:
FullArticle.objects.order_by('?').values('title','author','heroImage','body').first()
The above would result in something like:
{'title': 'Foo', 'author': 'Foo Author', ... }
I've also tacked on your suggestion of random =
FullArticle.objects.order_by('?')[0] called it "random" instead.
Not sure what this is about, but try to avoid shadowing built-in libraries, like random.
1) Actually you almost did it.
try:
article = FullArticle.objects.order_by('?')[0]
except IndexError:
article = None
2) You could use this in models.py as well as in views.py. IMHO there is no need to extract this string to separate method so I would write this code wherever I need it.
3) Better use ORM don't convert db result to list to choose first item. This is can be really memory and CPU expensive.
Getting a random article would usually be done in a view, or as a modelmanager method, or as a class method. Fullarticle.random should not be a class attribute. That will not work as you expect.
# Used in a view.
article = FullArticle.objects.order_by('?').first()
# you can also make a random() method in your model manager.
def random(self):
return self.get_queryset().order_by('?').first()
# or as a class method inside FullArticle
#classmethod
def random(cls):
return cls.objects.order_by('?').first()
I'm not quite sure what exactly you mean by this.
I only want data associated with the specific parts of the article: title, author, heroImage and body. How would I go about doing that?
To access specific attributes you do this:
title = article.title
author = article.author
If you don't need to use article.category, just don't access it.
from django.views.generic import DetailView
from books.models import Publisher, Book
To pass data from your (class based) View to the template it has to be added to the context.
Here's an example from the official documentation:
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
source: https://docs.djangoproject.com/en/1.7/topics/class-based-views/generic-display/#adding-extra-context
Lots of people find Class Based Views in Django to be a bit confusing. I would recommend that you understand how function based views work before you start doing anything complicated with CBVs.
I have the below models for which I'm trying to create a form for:
class Letter(models.Model):
title = models.CharField(max_length=100)
publish_date = models.TimeField()
class LetterRecipients(models.Model):
letter = models.ForeignKey(Letter)
recipient_name = models.CharField(max_length=200)
recipient_rating = models.IntegerField()
has_responded = models.BooleanField()
I'd like a single form that allows the user to enter a title and publish_date for the letter, and in the same form enter multiple recipients by name and rating.
Can anyone help with creating the form model for this? I can't figure out how to have django generate this form using {{ form.as_p }}. I think I'll have to use jQuery to create the additional recipient rows on the HTML page, but how would I get django to parse those into the model?
Any help would be greatly appreciated.
Thanks.
Ark
Ark, you can use ModelMultipleChoiceField in Django form. Here are some roughly example. I create "posts" apps just for quick testing :
forms.py
from django import forms
from django_test.posts.models import Letter, LetterRecipients
class LetterForm(forms.Form):
title = forms.CharField()
publish_date = forms.TimeField()
recepient = forms.ModelMultipleChoiceField(
queryset=LetterRecipients.objects.all()
)
models.py
from django.db import models
class Letter(models.Model):
title = models.CharField(max_length=100)
publish_date = models.TimeField()
class LetterRecipients(models.Model):
letter = models.ForeignKey(Letter)
recipient_name = models.CharField(max_length=200)
recipient_rating = models.IntegerField()
has_responded = models.BooleanField()
def __unicode__(self):
return self.recipient_name
views.py
# Create your views here.
from django_test.posts.forms import LetterForm
from django.shortcuts import render
def index(request):
form = LetterForm()
data = {'form': form}
return render(request, 'posts/index.html', data)
index.html
{% load url from future %}
{{ form.as_p }}