Django - unique_together option with override save method in form - django

I want to use the unique_together option when user save data within a form and not with the django admin.
models.py:
from django.db import models
from django.contrib.auth.models import User
class ezApp(models.Model):
name = models.SlugField(max_length=50, unique=True )
date_created = models.DateTimeField('date created', auto_now_add=True)
date_updated = models.DateTimeField('date updated', auto_now=True)
created_by = models.ForeignKey(User)
in_use = models.BooleanField()
class Meta:
unique_together = (('name', 'created_by'),)
forms.py
from django.forms import ModelForm
from django.forms.models import BaseModelFormSet
from ezApp.models import *
class BaseEzAppFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(BaseEzAppFormSet, self).__init__(*args, **kwargs)
def save_new(self, form, commit=True):
obj = form.save(commit=False)
obj.created_by = self.user
if commit:
obj.save()
return obj
views.py:
from django.shortcuts import *
from ezApp.models import *
from django.forms.models import modelformset_factory
from django.http import HttpResponseServerError
from ezApp.forms import *
def createEzAppInstance(request):
if request.method == 'POST':
ezAppFormSet = modelformset_factory(ezApp, extra=1, fields=('name'), formset=BaseEzAppFormSet)
formset = ezAppFormSet(request.POST, request.FILES, user=request.user)
if formset.is_valid():
formset.save()
return render_to_response("ezApp/manage_new_ezApp.html", {'formset': formset, 'title': "New App"}, context_instance=RequestContext(request))
else:
error_msg = u"You are not logged in"
return HttpResponseServerError(error_msg)
With unique_together in the Meta of the model, the validation is working only inside django admin but not when I use the form to save new data.

As msc points out you need to override the save method rather than writing your own.
def save(self, *args, **kwargs):
obj = super(BaseEzAppFormSet, self).save(form, *args, commit=False, **kwargs)
obj.created_by = self.user
obj.save()
return obj
It doesn't look like your save_new() method was ever being called in your view.

Related

In Django: Even after using "queryset = objects.none()" in forms.py, I still see the dropmenu with all the options

While rendering the form in web browser, the dropdown menu of CPA_Clients should be empty since I've used "self.fields['name'].queryset = CPA_Client.objects.none()" (Since I'll be making it chained dropdown) in forms.py file, but I still see all the contents of CPA_Clients in dropdown.
Where did I go wrong?
forms.py
from django import forms
from .models import Task, CPA_Client
class TaskDetails(forms.Form):
class Meta:
model = Task
fields = '__all__'
def __init__(self, *args, **kwargs):
super(TaskDetails, self).__init__(*args, **kwargs)
self.fields['name'].queryset = CPA_Client.objects.none()
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
from uuid import uuid4
from django.urls import reverse
import os
#Create your models here.
class TaskType(models.Model):
name = models.CharField(max_length=40)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Task Type'
class CPAsList(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'CPAs'
class CPA_Client(models.Model):
CPA = models.ForeignKey(CPAsList,on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'CPA\'s Clients'
def get_random_filename(instance, filename):
now = datetime.now()
path = now.strftime("%Y/%B/%d")
instance.filename = filename
ext = filename.split('.')[-1]
filename = "%s.%s" % (str(uuid4()), ext)
return os.path.join(path, filename)
TASK_STAGE = [
('NE','New'),
('OG','Ongoing'),
('CM','Completed')
]
PRIORITY = [
('HI','High'),
('ME','Medium'),
('LO','Low')
]
class Document(models.Model):
file = models.FileField(upload_to=get_random_filename, default="")
filename = models.CharField(max_length=128, editable=False, default="")
def __str__(self):
return self.filename
class Task(models.Model):
Stage = models.CharField(max_length=100, choices=TASK_STAGE, default='NE')
Subject_Line = models.CharField(max_length=255, default="", editable=True,)
Task_Type = models.ForeignKey(TaskType, on_delete=models.SET_NULL, null=True)
CPA = models.ForeignKey(CPAsList, on_delete=models.SET_NULL, null=True)
Client_of_CPA = models.ForeignKey(CPA_Client, on_delete=models.SET_NULL, null=True)
Priority = models.CharField(max_length=100, choices = PRIORITY, default='ME')
Assign_to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignedUser')
Date_Added = models.DateTimeField(editable=True, null=True, default=datetime.now)
Additional_Note = models.TextField(blank=True, null=True)
Author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assigneeUser', null=True, editable=False)
def __str__(self):
return self.Subject_Line
def get_absolute_url(self):
return reverse('task-detail', kwargs={'pk': self.pk})
class TimeStamps(models.Model):
Task_ID = models.ForeignKey(User, on_delete=models.CASCADE)
TimeStamp = models.IntegerField(default=0)
views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse, HttpResponse
from .models import Task
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from .models import Task, CPAsList, CPA_Client, TaskType, Document
from .forms import TaskDetails
from django.contrib.auth.decorators import login_required
# Create your views here.
#login_required
def home(request):
context = {
'tasks': Task.objects.all()
}
return render(request, 'crm/home.html', context)
class TaskListView(ListView):
model = Task
template_name = 'crm/home.html'
context_object_name = 'tasks'
ordering = ['-Date_Added']
class TaskDetailView(DetailView):
model = Task
class TaskDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Task
success_url = '/'
def test_func(self):
task = self.get_object()
if self.request.user == task.Author:
return True
return False
class TaskCreateView(LoginRequiredMixin, CreateView):
model = Task
fields = '__all__'
success_url = '/'
def model_form_upload(self, form):
if request.method == 'POST':
form = TaskDetails(request.POST, request.FILES)
if form.is_valid():
form.instance.Author = self.request.user
return super().model_form_upload(form)
else:
form = TaskDetails()
return render(request, 'crm-home', {'form': form})
class TaskUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Task
fields = '__all__'
#success_url = '/'
def model_form_upload(self, form):
if request.method == 'POST':
form = TaskDetails(request.POST, request.FILES)
if form.is_valid():
form.Author = self.request.user
return super().model_form_upload(form)
else:
form = TaskDetails()
return render(request, 'crm-home', {'form': form})
def test_func(self):
task = self.get_object()
if self.request.user == task.Author:
return True
return False
def contact(request):
return render(request, 'crm/contact.html')
def update_counter(request):
if request.method == 'POST':
counterValue = request.POST['counter']
startTime = request.POST['timestamp']
print(counterValue)
print(startTime)
message = 'update successful'
return HttpResponse(message)
#AJAX
def load_cities(request):
country_id = request.GET.get('country_id')
cities = City.objects.filter(country_id=country_id).all()
return render(request, 'persons/city_dropdown_list_options.html', {'cities': cities})
# return JsonResponse(list(cities.values('id', 'name')), safe=False)
Your Task model has no name field. The Client_of_CPA field is a ForeignKey to the CPA_Client model:
class TaskDetails(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['Client_of_CPA'].queryset = CPA_Client.objects.none()
class Meta:
model = Task
fields = '__all__'
Your TaskCreateView and TaskUpdateView do not use your form however, you need to specify this with:
class TaskCreateView(LoginRequiredMixin, CreateView):
model = Task
form_class = TaskDetails
success_url = '/'
template_name = 'crm-home'
def form_valid(self, form):
form.instance.Author = self.request.user
return super().form_valid(form)
class TaskUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Task
form_class = TaskDetails
success_url = '/'
template_name = 'crm-home'
def form_valid(self, form):
form.instance.Author = self.request.user
return super().form_valid(form)
def test_func(self):
task = self.get_object()
return self.request.user == task.Author

Cross queries in django

I have two models as below
class Watched(Stamping):
user = models.ForeignKey("User", null=True, blank=True, on_delete=models.CASCADE,
default=None)
count = models.PositiveIntegerField()
class Link(Stamping):
...
user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
url = models.CharField(max_length=256, default=None)
watched = models.ForeignKey(Watched, null=True, blank=True, on_delete=models.CASCADE, default=None)
...
My forms.py
class SimpleLink(forms.Form):
url = forms.URLField(max_length=256)
A user can create a Link object and when some conditions are met, the object will be added to Watched. The Watched model contains objects created by different users.
Now I want to filter the Watched class and grab only the objects created by the requesting user in the Link model but I don't know how I can achieve that. Any help will be appreciated.
A sample of what I want to achieve is...
Watched.objects.filter(Link.objects.filter(user=request.user). I know my sample is crazy. But from the outside query, I want to grab the Link objects created by user making the request
You need to limit the queryset in your ModelForm. A ModelForm will thus look like:
from django import forms
class LinkForm(forms.ModelForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is not None:
self.fields['watched'].queryset = Watched.objects.filter(
link__user=user
)
class Meta:
model = Link
fields = ['url', 'watched']
In our view, we can then set the user object:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
#login_required
def some_view(request):
if request.method == 'POST':
form = LinkForm(request.POST, user=request.user)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('name-of-some-form')
else:
form = LinkForm(user=request.user)
return render(request, 'some-template.html', {'form': form})
For a class-based view, we can override the .get_form_kwargs(…) method [Djangod-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
class SomeView(LoginRequiredMixin, CreateView):
form_class = LinkForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)

Django question on assigning the created news/team to the user who created it

please help me with one question, if possible.
I have a profile model that has a OneToOneField to User and there is a team field in the Profile model, there is also a Team model with a name, tag, etc. I would like to ask how to make the user who creates the team immediately be in it, so that the team field of the Profile model is assigned this team automatically, so that he is its creator and captain immediately. Maybe someone can help, explain, throw a banal example for understanding.
The creation was done like this, in a separate application. But I don't understand how to give the browser the created tim.
models.py
from django.db import models
from django.contrib.auth.models import User
from slugify import slugify
from django.urls import reverse
class BaseModel(models.Model):
objects = models.Manager()
class Meta:
abstract = True
class Profile(BaseModel):
user = models.OneToOneField(
User, on_delete=models.CASCADE, null=True, blank=True
)
nickname = models.CharField(max_length=30, unique=True, null=True)
team = models.ForeignKey('Team', on_delete=models.SET_NULL, blank=True, null=True)
def save(self, *args, **kwargs):
super(self.__class__, self).save(*args, **kwargs)
if self._state.adding is True:
Profile.objects.create()
def __str__(self):
return self.nickname
class Meta:
verbose_name = "Автор"
verbose_name_plural = "Авторы"
class Team(BaseModel):
name = models.CharField('Название', max_length=50)
tag = models.CharField('Тег', max_length=16, unique=True)
slug = models.SlugField(unique=True, blank=True, null=True)
def __str__(self):
return f'{self.name} [{self.tag}]'
def get_absolute_url(self):
return reverse("team_detail", kwargs={"slug": self.slug})
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Team, self).save(*args, **kwargs)
class Meta:
verbose_name = "Команда"
verbose_name_plural = "Команды"
forms.py
from django import forms
from django.contrib.auth.models import User
from django.forms import TextInput, Textarea, FileInput, IntegerField
from django.forms import TextInput, Textarea, FileInput, Select
from .models import *
class CreateTeamForm(forms.ModelForm):
class Meta:
model = Team
fields = {
'name', 'tag', 'slug'
}
views.py
from django.conf import settings
from django.contrib.auth import authenticate, login, get_user_model
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.shortcuts import render, redirect, resolve_url
from django.utils.http import url_has_allowed_host_and_scheme
from django.views.generic.base import View
from django.views.generic import DetailView, ListView
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.decorators.csrf import csrf_exempt
from .models import *
from .forms import *
# Create your views here.
class CreateTeam(View):
def get(self, request):
form = CreateTeamForm(request.POST)
context = {'form': form}
return render(request, 'team/home.html', context)
def post(self, request):
if request.method == 'POST':
form = CreateTeamForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
return redirect('home')
I'm just learning django, so it's hard to implement everything at once, and I'll be happy to help.
NEW CODE
forms.py
class JoinTeamForm(forms.ModelForm):
key = forms.CharField(label='key', max_length=20)
class Meta:
model = Team
fields = {'key'}
I tried without key = forms.CharField(label='key', max_length=20), but in html {{ form.key }} didn't work.
views.py
class JoinTeam(LoginRequiredMixin, View):
def get(self, request, pk):
print(f'post:{request.POST}, get:{request.GET}')
form = JoinTeamForm(request.POST or None)
team = Team.objects.get(id=pk)
context = {'form': form,
'team': team
}
return render(request, 'team/team_detail.html', context)
def post(self, request, pk):
print(f'post: {request.POST} team_id: {Team.objects.get(id=pk).key}')
profile = request.user.profile
error_msg = 'Неверный код'
if request.method == 'POST':
form = JoinTeamForm(request.POST)
role = Role.objects.get(id=2)
team = Team.objects.get(id=pk)
if form.is_valid():
key = form.save()
if key == team.key:
profile.team = team
profile.role = role
profile.save()
return redirect(team.get_absolute_url())
else:
return HttpResponse(error_msg)
return redirect(team.get_absolute_url())
Could you edit your view to update the user's profile after the team is created?
class CreateTeam(View):
def get(self, request):
form = CreateTeamForm(request.POST)
context = {'form': form}
return render(request, 'team/home.html', context)
def post(self, request):
profile = request.user.profile
if request.method == 'POST':
form = CreateTeamForm(request.POST)
if form.is_valid():
team = form.save()
profile.team = team
profile.save()
return redirect('home')
return redirect('home')
Please note, the way you have this set up is that each profile can only be on one team. If that's your intent, great, but if not you may want to set up a many to many model here so a user can be associated with multiple teams.

Django - How do I populate the user field of a model by default in the admin panel based on the current user logged in?

I am trying to create a question-answer forum in django where only the admins are able to respond to a question asked by all the registered users.
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
# Create your models here.
class Question(models.Model):
username=models.ForeignKey(User, on_delete=models.DO_NOTHING)
question=models.CharField(max_length=100)
date=models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.question
class Comments(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{}-{}'.format(self.question.question, str(self.user.username))
admin.py
from django.contrib import admin
from . models import Question, Comments
# Register your models here.
admin.site.register(Question)
admin.site.register(Comments)
views.py
from django.shortcuts import render, redirect
from . models import Question, Comments
from .forms import CommentForm
# Create your views here.
def addQuestion(request):
if request.method == 'POST':
username = request.user
question = request.POST['question']
question = Question(username=username, question=question)
question.save()
# note=Note(title=title, description=description, username=username)
# note.save()
return redirect('/dashboard')
else:
return render(request, "dashboard/question.html")
def viewQuestion(request, question_id):
viewquestion=Question.objects.get(id=question_id)
comments = Comments.objects.filter(question=viewquestion).order_by('-question_id')
context = {
'viewquestion':viewquestion,
'comments':comments
}
return render (request, 'dashboard/questionview.html', context)
As of now, the admin panel provides a drop down based on which I can select a user, but I need the model to display the authenticated admin user by default in the model before adding a comment rather than an admin manually choosing the username.
This is how it looks like currently.
How do I make the dropdown select the current logged in user by default?
Step 1:-
# Pass request params to your model form
admin.py
class CommentsAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
ModelForm = super(CommentsAdmin, self).get_form(request, obj, **kwargs)
class ModelFormMetaClass(ModelForm):
def __new__(cls, *args, **kwargs):
kwargs['request'] = request
return ModelForm(*args, **kwargs)
return ModelFormMetaClass
fields = (('question'), ('user'), ('content',),)
form = CommentsForm
admin.site.register(Comments, CommentsAdmin)
Step 2:-
# Create your form which you have specified for your admin class of comments model (CommentsAdmin)
form.py
class CommentsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(CommentsForm, self).__init__(*args, **kwargs)
self.fields['user'].initial = self.request.user
class Meta:
model = Comments
exclude = ()

How to bined a detailview field to a form field in django and save into database

I am building this simple system for online voting, where people can vote but will have to pay for their vote cast.My current challenge is how to bined a selected candidate on a page and allowing voters to enter number of votes they went to cast for that candidate on the same page
I have tried using SingleObjectMixin but am having problem saving the form in database.
And also how can i prepopulate the select candidate to a model field name Nominee
model.py
class Award(models.Model):
STATUS_PUBLISHED = (
('Closed', 'Closed'),
('Opened', 'Opened'),
)
slug = models.SlugField(max_length=150)
name = models.CharField(max_length=100)
date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='award_images')
status = models.CharField(max_length=20, choices=STATUS_PUBLISHED, default='Closed')
def __str__(self):
return self.name
class Category(models.Model):
Award = models.ForeignKey(Award, on_delete=models.CASCADE)
category = models.CharField(max_length=100,)
slug = models.SlugField(max_length=150)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.category
class Nomination(models.Model):
Fullname = models.CharField(max_length=120)
Category = models.ForeignKey(Category, on_delete=models.CASCADE)
votes = models.IntegerField(default=0)
date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=150)
image = models.ImageField(upload_to='nominations_images')
def __str__(self):
return self.Fullname
class VoteAmount(models.Model):
Nominee = models.ForeignKey(Nomination, on_delete=models.CASCADE)
votes_amount = models.IntegerField(default=0)
def __str__(self):
return self.votes_amount
views.py
from django.views.generic import ListView, DetailView, CreateView
from .models import Award, Category, Nomination, VoteAmount
from .forms import VoteAmountForm
from django.urls import reverse
from django.http import HttpResponseForbidden
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin
from django.views import View
class AwardView(ListView):
template_name = 'award.html'
context_object_name = 'award_list'
queryset = Award.objects.filter(status='Opened').order_by('-date')
class CategoryView(DetailView):
model = Award
template_name = 'category.html'
class NominationView(DetailView):
model = Category
template_name = 'nomination.html'
class VoteAmountView(DetailView):
model = Nomination
template_name = 'voteamount.html'
def get_context_data(self, **kwargs):
context = super(VoteAmountView, self).get_context_data(**kwargs)
context['form'] = VoteAmountForm()
return context
class AmountView(SingleObjectMixin, FormView):
template_name = 'voteamount.html'
form_class = VoteAmountForm
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
return super(AmountView, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('results', kwargs={'pk': self.object.pk})
class AuthorDetail(View):
def get(self, request, *args, **kwargs):
view = VoteAmountView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = AmountView.as_view()
return view(request, *args, **kwargs)
class PaymentView(DetailView):
model = VoteAmount
template_name = 'PaymentView.html'
form.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import VoteAmount
class VoteAmountForm(forms.ModelForm):
class Meta:
model = VoteAmount
fields = ('Nominee', 'votes_amount')
ImproperlyConfigured at /results/fiifi-quansa
AmountView is missing a QuerySet. Define AmountView.model, AmountView.queryset, or override AmountView.get_queryset().
all i went to do is show the selected condidate as a nominee field for model VoteAmount and enter my number of votes
As the error said:
AmountView is missing a QuerySet. Define AmountView.model, AmountView.queryset, or override AmountView.get_queryset().
Means, you are using SingleObjectMixin, you need to provide a queryset/model/ override get_queryset() method in the class. like this:
class AmountView(SingleObjectMixin, FormView):
template_name = 'voteamount.html'
form_class = VoteAmountForm
queryset = Nomination.objects.all()
Also, you need to override get_form_kwargs() method to load the the initial nominee data.
class AmountView(SingleObjectMixin, FormView):
template_name = 'voteamount.html'
form_class = VoteAmountForm
queryset = Nomination.objects.all()
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super().get_form_kwargs(*args, **kwargs)
form_kwargs['nominee'] = self.get_object()
return form_kwargs
And override VoteAmountForm as well to limit the choices:
class VoteAmountForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
nominee = kwargs.pop('nominee')
super().__init__(*args, **kwargs)
self.fields['Nominee'].queryset = Nominee.objects.filter(pk=nominee.pk) # <-- Please use snake_case when defining model field names(as per pep8 style guide)
self.initial['Nominee'] = nominee
class Meta:
model = VoteAmount
fields = ('Nominee', 'votes_amount')