Django ownership assignment to modify or delete entries - django

First post here. I followed the project code in the Python Crash Course book, so if the code looks similiar that is why.
In my django project, I want all the users to be able to view everything on the website. However, I want to have only the users who entered information in the entries to be able to edit and delete said entries. I know I need to assign ownership, but I'm having difficulty assigning it. currently I have a default setting enabled. For some reason if I take out the default it messes everything up.
urls.py
"""Defines URL patterns for research_topic_database."""
# research_topic_database
from django.urls import path
from . import views
app_name = 'research_topic_database'
urlpatterns = [
# Home page
path('', views.index, name='index'),
# Page that shows all the research category topics.
path('topics/', views.topics, name='topics'),
# Detail page for a single research entry submission.
path('topics/<int:topic_id>/', views.topic, name='topic'),
# Page for adding a new research entry submission.
path('new_entry/<int:topic_id>/', views.new_entry, name='new_entry'),
# Page for editing an entry.
path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'),
]
models.py
class Topic(models.Model):
"""A research category for topic submissions to fall under."""
category = models.CharField(max_length=19, choices=CATEGORY_CHOICES, blank=True)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model."""
return self.category
class Entry(models.Model):
"""Entry information about a research topic."""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
classification = models.CharField(max_length=10, choices=CLASSIFICTION_CHOICES, blank=True)
# The research_title identifies the entry in the database.
research_title = models.CharField(max_length=200)
research_topic_description = models.CharField(max_length=200)
# Explain the USMC requirements you want the research to fulfill.
extended_topic_description = models.TextField()
desired_objectives = models.TextField()
sponsor = models.CharField(max_length=100)
point_of_contact = models.CharField(max_length=100)
mailing_address = models.CharField(max_length=200)
email_address = models.CharField(max_length=50)
phone = models.CharField(max_length=12)
fax = models.CharField(max_length=12)
desired_completion_date = models.CharField(max_length=12)
available_funding = models.CharField(max_length=50)
comments = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
# connecting research entries to certain Users.
owner = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
# owner = models.ForeignKey(User, on_delete=models.CASCADE)
# owner = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Return a string representation of the model."""
return f"{self.research_title[:100]}..."
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.http import Http404
from .models import Topic, Entry
from .forms import EntryForm
def index(request):
"""The home page for research_topic_database."""
return render(request, 'research_topic_database/index.html')
#login_required
def topics(request):
"""Show all research categories from TOPIC class."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'research_topic_database/topics.html', context)
#login_required
def topic(request, topic_id):
"""Show a single research category from TOPIC class
and all its submission entries."""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'research_topic_database/topic.html', context)
#login_required
def new_entry(request, topic_id):
"""Add a new entry submission under a research category."""
topic = Topic.objects.get(id=topic_id)
if request.method != 'POST':
# No data submitted; create a blank form.
form = EntryForm()
else:
# POST data submitted; process data.
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('research_topic_database:topic', topic_id=topic_id)
# Display a blank or invalid form
context = {'topic': topic, 'form': form}
return render(request, 'research_topic_database/new_entry.html', context)
#login_required
def edit_entry(request, entry_id):
"""Edit an existing research entry submission."""
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if request.method != 'POST':
# Initial request, pre-fill form with current entry.
form = EntryForm(instance=entry)
else:
# POST data submitted; process data.
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return redirect('research_topic_database:topic', topic_id=topic.id)
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'research_topic_database/edit_entry.html', context)

Related

AssertionError: 200 != 302. Trying to test a redirect from a successful form entry

I'm trying to test redirect from a form using POST. I'm following the mdn web doc tutorial for Django and thought I would see if I could do some of my own test which are not in the tutorial. I'm try to test the book creation form in the tutorial to see if it redirects to book details page which it should do automatically because it's a class based generic view. It works correctly when I test locally but I cannot get the testcase to pass. Thanks for the help in advance.
This is the error:
> =====================================================================
FAIL: test_redirects_to_book_details_on_success (catalog.tests.test_views.BookCreateView)
> ----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\makor\mypythonscripts\django_projects\locallibrary\catalog\tests\test_views.py", line 399, in test_redirects_to_book_details_on_success
self.assertRedirects(response, reverse('book-detail', kwargs={'pk': 1}))
File "C:\Users\makor\mypythonscripts\django_projects\venv\lib\site-packages\django\test\testcases.py", line 512, in assertRedirects
self.assertEqual(
AssertionError: 200 != 302 : Response didn't redirect as expected: Response code was 200 (expected 302)
----------------------------------------------------------------------
Ran 1 test in 0.341s
FAILED (failures=1)
Destroying test database for alias 'default'...
models.py
from django.db import models
from django.urls import reverse # Used to generate URLs by reversing the URL pattern.
from django.contrib.auth.models import User
from datetime import date
import uuid # Required for unique book instances
class Genre(models.Model):
"""Model representing book genre."""
name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
def __str__(self):
"""String representation of Model object."""
return self.name
class Language(models.Model):
"""Model representing a langauge."""
LANGUAGES = (
('English', 'English'),
('French', 'French'),
('German', 'German'),
('Spanish', 'Spanish'),
)
language = models.CharField(max_length=20, choices=LANGUAGES, blank=False, default='English', help_text='Availble languages')
def __str__(self):
"""String representation of Model object."""
return self.language
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
# Foreign Key used because book can only have one author, but author can have multiply books.
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book.')
isbn = models.CharField('ISBN', max_length=13, unique=True,
help_text='13 character ISBN number')
# ManyToManyField used because genre can contain many books. Books can cover many genres.
# Genre class has already been defined, so we can specify the object above.
genre = models.ManyToManyField('Genre', help_text='Select a genre for this book.')
language = models.ManyToManyField('Language', help_text='Select the langauge.')
def __str__(self):
"""String for representing the Model object."""
return self.title
def get_absolute_url(self):
"""Returns the URL to access a detail record for this book."""
return reverse('book-detail', args=[str(self.id)])
def display_genre(self):
"""Create a string for the Genre. This is required to display genre in Admin."""
return ', '.join(genre.name for genre in self.genre.all()[:3])
display_genre.short_description = 'Genre'
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text='Unique ID for this particular book across the whole library.')
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200, blank=True)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
LOAN_STATUS = (
('m', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='m',
help_text='Book availability',
)
class Meta:
ordering = ['due_back']
permissions = (('can_mark_returned', 'set book as returned'),)
def __str__(self):
"""String representing the Model object."""
return f'{self.id} ({self.book.title})'
#property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the URL to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String representing the Model object."""
return f'{self.last_name}, {self.first_name}'
views.py
import datetime
from django.views import generic
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse, reverse_lazy
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.decorators import login_required, permission_required
from . models import Book, Author, BookInstance, Genre, Language
from . forms import RenewBookForm
def index(request):
"""View function for home page of site."""
# Generate counts of some main objects.
num_books = Book.objects.all().count()
num_instances = BookInstance.objects.all().count()
# Available books (status = 'a')
num_instances_available = BookInstance.objects.filter(status__exact='a').count()
# The 'all()' is implied by default.
num_authors = Author.objects.count()
# Number of visits to this view, as counted in the session variable.
num_visits = request.session.get('num_visits', 0)
request.session['num_visits'] = num_visits + 1
# Generate counts for genre.
num_genre = Genre.objects.all().count()
# Filter books containing a particular word.
title = 'game of thrones'
num_title = Book.objects.filter(title__contains=title.lower()).count()
context = {
'num_books': num_books,
'num_instances': num_instances,
'num_instances_available': num_instances_available,
'num_authors': num_authors,
'num_genre': num_genre,
'num_title': num_title,
'num_visits': num_visits,
}
# Render the HTML template index.html with the data in the context variable.
return render(request, 'index.html', context=context)
class BookListView(generic.ListView):
model = Book
paginate_by = 10
class BookDetailView(generic.DetailView):
model = Book
class AuthorListView(generic.ListView):
model = Author
paginate_by = 10
class AuthorDetailView(generic.DetailView):
model = Author
class LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView):
"""Generic class-based view listing books on loan to current user."""
model = BookInstance
template_name = 'catalog/bookinstance_list_borrowed_user.html'
paginate_by = 10
def get_queryset(self):
return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')
class BorrowedListView(PermissionRequiredMixin, generic.ListView):
"""Generic class-based view listing all books, requires correct permissions."""
permission_required = 'catalog.can_mark_returned'
model = BookInstance
template_name = 'catalog/borrowed_list.html'
paginate_by = 10
def get_queryset(self):
return BookInstance.objects.filter(status__exact='o').order_by('due_back')
#login_required
#permission_required('catalog.can_mark_returned', raise_exception=True)
def renew_book_librarian(request, pk):
book_instance = get_object_or_404(BookInstance, pk=pk)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = RenewBookForm(request.POST)
# Check if the form is valid
if form.is_valid():
# Process the data in form.cleaned_data as required (here we just write it to the model due_back field)
book_instance.due_back = form.cleaned_data['renewal_date']
book_instance.save()
# Redirect to a new URL:
return HttpResponseRedirect(reverse('all-borrowed'))
else:
context = {
'form': form,
'book_instance': book_instance,
}
return render(request, 'catalog/book_renew_librarian.html', context)
# If this is a GET (or any other method) create the default form.
else:
proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})
context = {
'form': form,
'book_instance': book_instance,
}
return render(request, 'catalog/book_renew_librarian.html', context)
class AuthorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
permission_required = 'catalog.can_mark_returned'
model = Author
fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']
initial = {'date_of_death': '11/06/2022'}
class AuthorUpdate(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
permission_required = 'catalog.can_mark_returned'
model = Author
fields = '__all__' # Not recommended (potential security issue if more fields are added.)
class AuthorDelete(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
permission_required = 'catalog.can_mark_returned'
model = Author
success_url = reverse_lazy('authors')
class BookCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
permission_required = 'catalog.can_mark_returned'
model = Book
fields = ['title', 'author', 'summary', 'isbn', 'genre', 'language']
class BookUpdate(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
permission_required = 'catalog.can_mark_returned'
model = Book
fields = '__all__'
class BookDelete(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
permission_required = 'catalog.can_mark_returned'
model = Book
success_url = reverse_lazy('books')
test.py
class BookCreateView(TestCase):
def setUp(self):
# Create a user
test_user1 = User.objects.create_user(username='testuser1', password='1X<ISRUkw+tuK')
test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&3iD')
test_user1.save()
test_user2.save()
# Give test_user2 permission to create Book.
permission = Permission.objects.get(name='set book as returned')
test_user2.user_permissions.add(permission)
test_user2.save()
test_author = Author.objects.create(first_name='John', last_name='Smith')
test_genre = Genre.objects.create(name='Fantasy')
test_language = Language.objects.create(language='English')
test_author.save()
test_genre.save()
test_language.save()
self.author = Author.objects.get(id=1)
self.genre = Genre.objects.get(id=1)
self.language = Language.objects.get(id=1)
def test_redirect_if_not_logged_in(self):
response = self.client.get(reverse('book-create'))
# Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.url.startswith('/accounts/login/'))
def test_forbidden_if_logged_in_but_not_correct_permission(self):
login = self.client.login(username='testuser1', password='1X<ISRUkw+tuK')
response = self.client.get(reverse('book-create'))
self.assertEqual(response.status_code, 403)
def test_logged_in_with_permission_to_create_book(self):
login = self.client.login(username='testuser2', password='2HJ1vRV0Z&3iD')
response = self.client.get(reverse('book-create'))
# Check that it lets us login - this is our book and we have the right permissions.
self.assertEqual(response.status_code, 200)
def test_view_uses_correct_template(self):
login = self.client.login(username='testuser2', password='2HJ1vRV0Z&3iD')
response = self.client.get(reverse('book-create'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'catalog/book_form.html')
def test_redirects_to_book_details_on_success(self):
login = self.client.login(username='testuser2', password='2HJ1vRV0Z&3iD')
response = self.client.post(reverse('book-create'), {'title': 'Test',
'author': self.author,
'summary': 'It\'s all good',
'isbn': '1234567',
'genre': self.genre,
'language': self.language,
})
self.assertRedirects(response, reverse('book-detail', kwargs={'pk': 1}))
form template
{% extends 'base_generic.html' %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit" />
</form>
{% endblock %}
Screenshot of form
I have been at this for about two days but I think my beginners luck has ran out. I can usually figure it out by myself after a while but not anymore as this is my first post. I think the data i am entering into the client.post is not working and giving the correct response object. Sorry if my lingo is not correct as I am new to all this.

Django POST save and delete

I'm attempting to save records into a model using the modelform (this part is working as intended) and delete records from the same model using a checkbox (can't figure this piece out). I am creating a comprehensive view so I'm not creating using a def variable(reqeust, id) function and my intention is to have both POST methods redirect back to the same page. How would I go about deleting a model record using a checkbox and POST? I will add an #login_required decorator later. Here is my code:
models.py:
class ReportDirectory(models.Model):
report_name = models.CharField(max_length=300, unique=True, blank=False)
report_desc = models.TextField()
report_type = models.CharField(max_length=300)
report_loc = models.TextField()
slug = models.SlugField(unique=True, max_length=300)
last_update = models.DateTimeField(null=True)
main_tags = models.CharField(max_length=300)
# Renames the item in the admin folder
def __str__(self):
return self.report_name
class Favorite(models.Model):
directory_user = models.ForeignKey(User, on_delete=models.CASCADE)
user_report = models.ForeignKey(ReportDirectory, on_delete=models.CASCADE)
favorited = models.BooleanField()
def __str__(self):
return str(self.directory_user)+" - "+str(self.user_report)
views.py:
from django.shortcuts import render,redirect
from django.views import generic
from .models import ReportDirectory, Favorite
from django.contrib.auth.models import User
from .forms import FavoriteForm
def report_directory(request):
favorite = Favorite.objects.filter(directory_user=request.user.id, favorited=True)
reports = ReportDirectory.objects.exclude(favorite__directory_user=request.user.id, favorite__favorited=True)
favform = FavoriteForm(initial={'directory_user':request.user,},)
context = {
'reports':reports,
'favorite':favorite,
'favform':favform
}
if request.method =='POST' and 'favorited' in request.POST:
form = FavoriteForm(request.POST)
if form.is_valid():
form.save()
return redirect('/report_directory')
else:
form = FavoriteForm()
elif request.method =='POST' and 'deletefav' in request.POST:
Favorite.objects.filter(id=id).delete()
return redirect('/report_directory')
return render(request, 'counter/report_directory.html',context)

Django : Can't auto insert user ID into other form

I am trying to get user/customer info and copy it automatically to new model via custom form once requested but keep getting error. General overview as below.
(error : ValueError at /create_request/
Cannot assign "<SimpleLazyObject: <User: kambing>>": "OrderRequest.user" must be a "Customer" instance.)
model relationship between customer model and request model
my views.py as below:
from django.shortcuts import render, redirect
from .models import *
from accounts.models import *
from .forms import CreateRequestForm
from django.contrib.auth.decorators import login_required
from accounts.models import *
#login_required(login_url='login')
def createRequest(request):
form = CreateRequestForm()
if request.method =='POST':
form = CreateRequestForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.user = request.user
order.save()
return redirect('/')
context = {'form':form}
return render(request, 'commerce_autoparts/request_form.html', context)
here my model.py(autopart_ecommerce): Same as shown in attached image
from django.db import models
from accounts.models import Customer
class Logistic(models.Model):
logistic_method = models.CharField(max_length=30, null=True,
blank=True)
def __str__(self):
return self.logistic_method
class OrderRequest(models.Model):
user = models.ForeignKey(Customer, on_delete=models.CASCADE,
null=True, blank=True)
ref_code = models.CharField(max_length=15, null=True, blank=True)
link = models.URLField(null=True, blank=True)
image = models.ImageField(upload_to='media/images',null=True,
blank=True)
price = models.FloatField(null=True)
draft = models.BooleanField(default=True)
logistic_method = models.ForeignKey(Logistic,
on_delete=models.CASCADE, null=True, blank=True)
note = models.TextField(max_length=100)
date_order = models.DateTimeField(auto_now=True)
I tried many solutions from stackoverflow but could not get it right. I am sorry if this not challenging question but I am stuck with this issue for some weeks. Some sort of guidance or reference available would be helpful.
I hope shared info is enough for your references.
Thank you
The problem is that it is a different model, so it doesn't like that. What you can do is copy the field data of the user to the customer, so you actually create new customer.
example:
# instead of this
order.user = request.user
order.save()
# you could do
new_user = Customer()
new_user.name = request.user.name
new_user.email = request.user.email
new_user.last_name = request.user.last_name
# then pass the user to the order
order.user = new_user
order.save()
I somehow manage to solve the issue. It was simply i don't refer the user to customer model. By adding the customer model (instance.user = request.user.customer), it automatically save the customer id once submitted. it may not be the perfect solution, but works for now..
#login_required(login_url='login')
def createRequest(request):
form = CreateRequestForm(request.POST)
if request.method =='POST':
form = CreateRequestForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user.customer
instance.ref_code = get_ref_code()
instance.save()
return redirect('/')

Django Model Terms of Service With Timestamp, Username, and Prompt User To Agree To Updated Terms of Service

I would like to create a model that contains a timestamp and the allauth currently logged in user who agreed to the Terms of Service. Then, on every page (if the user is logged in), annotate if the user has agreed to the latest Terms of Service (by comparing the timestamp of their last agreement to the timestamp of the latest updated Terms of Service), and if the user has not agreed to the most recent Terms of Service version they are redirected to a page that requires them to agree to the updated version. Then it redirects the user back to whence they came after they agree.
How does one go about creating something like this?
What I have so far is below.
Models.py:
from django.contrib.auth.models import User
class TermsOfService(models.Model):
agreement = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True,blank=True, null=True)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
def __str__(self):
return self.agreement
class UserMembership(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
stripe_customer_id = models.CharField(max_length=40, unique=True)
membership = models.ForeignKey(
Membership, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.user.username
Forms.py:
from .models import TermsOfService
class TermsOfServiceForm(forms.ModelForm):
class Meta:
model = TermsOfService
fields = ('agreement',)
def __init__(self, *args, **kwargs):
super(TermsOfServiceForm, self).__init__(*args, **kwargs)
self.fields['agreement'].widget.attrs={ 'id': 'agreement_field', 'class': 'form-control', 'required': 'true', 'autocomplete':'off'}
App Urls.py:
from django.urls import path
from .views import ( terms_of_service_view )
app_name = 'app'
urlpatterns = [ path('terms_of_service_view/', terms_of_service_view, name='terms_of_service_view'), ]
Views.py:
def get_user_membership(request):
user_membership_qs = UserMembership.objects.filter(user=request.user)
if user_membership_qs.exists():
return user_membership_qs.first()
return None
def terms_of_service_view(request):
if request.method == 'POST':
form = TermsOfServiceForm(request.POST)
if form.is_valid():
user_membership = get_user_membership(request)
instance = form.save(commit=False)
instance.user = request.user
instance.save()
context = {
'user_membership': user_membership,
'form':form
}
return render(request, "index.html", context)
else:
form = TermsOfServiceForm()
context = {
'user_membership': user_membership,
'form': form,
}
return render(request, "index.html", context)
A question arises from your code, like how are you going to determine when user needs to agree to agreement, do you create a bunch of new entry in TermsOfService. Rather than that, why not create a new model named Terms and add it as ForeignKey.
class Term(models.Model):
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True,blank=True, null=True)
# blah blah
class TermsOfService(models.Model):
term = models.ForeignKey(Term, on_delete=models.DO_NOTHING)
agreement = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True,blank=True, null=True)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
There is an advantage of taking this approach, that is all you need to do is create a new Term object, and rest can be taken care of by middleware. For example:
from django.urls import reverse
from django.shortcuts import redirect
class TermAgreeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if not request.user.is_authenticated:
return response
term_date = Term.objects.last().created_at
user_term_date = request.user.termofservice_set.filter(created_at__gte=term_date).exists()
if not user_term_date:
return redirect(reverse('app:terms_of_service_view')+'?next='+request.path)
return response
And update the view:
def terms_of_service_view(request):
if request.method == 'POST':
form = TermsOfServiceForm(request.POST)
if form.is_valid():
user_membership = request.user.usermembership # you don't need another view as User has OneToOne relation with UserMembership
instance = form.save(commit=False)
instance.user = request.user
instance.term = Term.objects.last()
instance.save()
go_next = request.GET.get('next', None) # handle redirection
if go_next:
return redirect(go_next)
context = {
'user_membership': user_membership,
'form':form
}
return render(request, "index.html", context)
else:
form = TermsOfServiceForm()
context = {
'user_membership': user_membership,
'form': form,
}
return render(request, "index.html", context)
Finally add that TermAgreeMiddleware in MIDDLEWARE settings. So everytime you want users to agree a new term, just create a new Term instance(from admin site or shell).

Save modelForm to update existing record

I create a modelForm with instance to existing model (Book). I am not able to update the Books record. Adding a new record is fine but when I attempt to update, it appears to be unable to find the publisher (which is a foreign key). Error is "No Publisher matches the given query."
models.py
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Meta:
ordering = ["name"]
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail')
objects = models.Manager()
sel_objects=AuthorManager()
def __unicode__(self):
return self.first_name+' '+ self.last_name
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField(blank=True, null=True)
num_pages = models.IntegerField(blank=True, null=True)
class BookForm(ModelForm):
class Meta:
model = Book
views.py
def authorcontactupd(request,id):
if request.method == 'POST':
a=Author.objects.get(pk=int(id))
form = AuthorForm(request.POST, instance=a)
if form.is_valid():
form.save()
return HttpResponseRedirect('/contact/created')
else:
a=Author.objects.get(pk=int(id))
form = AuthorForm(instance=a)
return render_to_response('author_form.html', {'form': form})
error msg
Page not found (404)
Request Method: POST
Request URL: http://127.0.0.1:8000/books/bookupd/
No Publisher matches the given query.
You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
urls.py
from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template
from mysite10.books.views import about_pages, books_by_publisher, authorcontact,bookcontact, booklisting, authorcontactupd
from django.views.generic import list_detail
from mysite10.books.models import Publisher, Book
from django.contrib import admin
admin.autodiscover()
def get_books():
return Book.objects.all()
publisher_info = {
'queryset': Publisher.objects.all(),
'template_name':'books/publisher_publisher_list_page.html',
'template_object_name': 'publisher',
'extra_context': {'book_list': Book.objects.all},
}
book_info = {
'queryset': Book.objects.order_by('-publication_date'),
'template_name':'books/publisher_publisher_list_page.html',
'template_object_name': 'book',
'extra_context': {'publisher_list': Publisher.objects.all},
}
oreilly_books = {
'queryset': Book.objects.filter(publisher__name="O'Reilly"),
'template_name':'books/publisher_publisher_list_page.html',
'template_object_name': 'book',
'extra_context': {'publisher_list': Publisher.objects.all},
}
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^polls/', include('mysite10.polls.urls')),
(r'^search-form/$', 'mysite10.views.search_form'),
(r'^search/$', 'mysite10.views.search'),
(r'^contact/$', 'mysite10.contact.views.contact'),
(r'^contact/thanks2/(\d+)$', 'mysite10.contact.views.thanks2'),
(r'^contact/thanks/$', 'mysite10.contact.views.thanks'),
(r'^publishers/$', list_detail.object_list, publisher_info),
(r'^books/$', list_detail.object_list, book_info),
(r'^books/oreilly/$', list_detail.object_list, oreilly_books),
(r'^books/(\w+)/$', books_by_publisher),
(r'^author/$', authorcontact),
(r'^authorupd/(\d+)/$', authorcontactupd),
(r'^contact/created/$', 'mysite10.books.views.created'),
(r'^bookform/$', bookcontact),
(r'^contact/bookscreated/$', 'mysite10.books.views.books_created'),
(r'^booklist/$', 'mysite10.books.views.booklisting'),
(r'^books/bookupd/(\d+)$', 'mysite10.books.views.book_upd'),
)
-------------------------------------------------
I finally got it working with below codes.
error in urls.py because of missing forward slash before $.
Amended to (r'^books/bookupd/(\d+)/$'
views.py
def book_upd(request,id):
if request.method == 'POST':
a=Book.objects.get(pk=int(id))
form = BookForm(request.POST, instance=a)
if form.is_valid():
form.save()
return HttpResponseRedirect('/contact/bookscreated')
else:
a=Book.objects.get(pk=int(id))
form = BookForm(instance=a)
return render_to_response('book_form.html', {'form': form})
urls.py
(r'^books/bookupd/(\d+)/$', 'mysite10.books.views.book_upd'),
There's some missing information like what you have in your urls.py. Can you post it as well? Did you check in the database that the record was actually not updated? (the error might be a result of processing the redirect)
Your edit is not sufficient:
- did you check the databse to see if the record is updated?
- Please paste the entire urls.py as for example it is interesting to see what /contact/created is mapped to in case it did succeed, or whether you have some publisher.get() methods in it
In addition the traceback can also provide lots of useful information as to the source of the problem.
did you check if the object is updated in the database even though you get the error?
Can you try removing the "oreilly_books" section (or at least the queryset part) and try doing the same without it?