I'm trying to make inline forms with class based views, i followed the instrunctions from here. The only change i've made was to give self.request.user instead of self.object to instance:
models.py
class Client(models.Model):
user = models.OneToOneField(CustomUser)
translate = models.BooleanField(default=False)
class ClientData(models.Model):
client = models.ForeignKey(Client)
language = models.ForeignKey(Language)
forms.py
class ClientForm(ModelForm):
class Meta:
model = Client
fields = '__all__'
exclude = ['user', ]
class ClientDataForm(ModelForm):
class Meta:
model = ClientData
fields = '__all__'
exclude = ['client', ]
ClientFormSet = inlineformset_factory(Client, ClientData, form=ClientDataForm, extra=1)
views.py
class ClientView(LoginRequiredMixin, UpdateView):
model = Client
fields = '__all__'
success_url = reverse_lazy('core:index')
class ClientDataView(LoginRequiredMixin, UpdateView):
template_name = 'core/client_data.html'
model = ClientData
form_class = ClientDataForm
success_url = reverse_lazy('core:index')
def get_object(self, queryset=None):
profile = get_object_or_404(ClientData, client__user=self.request.user)
return profile
def get_context_data(self, **kwargs):
context = super(ClientDataView, self).get_context_data(**kwargs)
if self.request.POST:
context['client_data'] = ClientFormSet(self.request.POST, instance=self.get_object())
else:
context['client_data'] = ClientFormSet(instance=self.get_object())
return context
def form_valid(self, form):
context = self.get_context_data()
client_data = context['client_data']
with transaction.atomic():
self.object = form.save()
if client_data.is_valid():
client_data.instance = self.object
return super(ClientDataView, self).form_valid(form)
Whenever i try to enter the page i get:
ValueError: Cannot query "asd#gmail.com": Must be "Client" instance.
[13/Dec/2017 15:48:36] "GET /client-data/ HTTP/1.1" 500 143759
for this line:
context['client_data'] = ClientFormSet(instance=self.get_object())
Your get_object is returning a ClientData instance.
def get_object(self, queryset=None):
profile = get_object_or_404(ClientData, client__user=self.request.user)
return profile
However, as the error suggests, the instance should be a Client instance, for example:
def get_object(self, queryset=None):
profile = get_object_or_404(Client, user=self.request.user)
return profile
Or you can simply follow the one-to-one relation backwards:
def get_object(self, queryset=None):
return self.request.user.client
Related
I want that when I try to create a new account, the site_name field is prepopulated with the site that has it
for instance, an account under reddit should have a site_name prepopulated with reddit.com already, instead of having to pick from the list of sites.
Here is my class view for account creating an account:
class AccountCreateView(LoginRequiredMixin, CreateView):
model = Account
template_name = "account_new.html"
fields = (
"site_name",
"username",
"password",
)
The rest of my views:
class HomePageView(ListView):
model = Site
template_name = "home.html"
def get_queryset(self):
try:
return Site.objects.filter(author=self.request.user)
except:
return HttpResponse("Login please!")
class SiteDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
model = Site
template_name = "site_detail.html"
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
class SiteDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Site
template_name = "site_delete.html"
success_url = reverse_lazy("home")
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
class SiteEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Site
template_name = "site_edit.html"
fields = ("siteName",)
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
class AccountDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Account
template_name = "account_delete.html"
slug_field = "slug_field"
slug_url_kwarg = "slug_field"
success_url = reverse_lazy("home")
class AccountEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Account
template_name = "account_edit.html"
fields = (
"username",
"password",
)
slug_field = "slug_field"
slug_url_kwarg = "slug_field"
class SiteCreateView(LoginRequiredMixin, CreateView):
model = Site
template_name = "site_new.html"
fields = ("siteName",)
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
models.py:
from django.db import models
from django.urls import reverse
from django.conf import settings
from django.template.defaultfilters import slugify
class Site(models.Model):
siteName = models.CharField(max_length=200)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
slug = models.SlugField(null=False, unique=True)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.siteName
def get_absolute_url(self):
return reverse("site_detail", kwargs={"slug": self.slug})
def save(self, *args, **kwargs):
if not self.slug:
slug_str = "%s %s" % (self.siteName, self.author)
self.slug = slugify(slug_str)
super(Site, self).save()
class Account(models.Model):
site_name = models.ForeignKey(Site, on_delete=models.CASCADE)
username = models.CharField(max_length=140)
password = models.CharField(max_length=50)
slug_field = models.SlugField(null=False, unique=True)
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse("home")
def save(self, *args, **kwargs):
if not self.slug_field:
self.slug_field = slugify(self.username)
return super().save(*args, **kwargs)
admin.py:
from django.contrib import admin
from .models import Site, Account
class SiteAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("siteName",)}
class AccountAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug_field": ("username",)}
admin.site.register(Site, SiteAdmin)
admin.site.register(Account, AccountAdmin)
A picture of creating a new account under redditcom, still having to specify the site_name:
createaccount picture
How do I access a current logged in user from a class-based view?
In a function-based view we can pass a request parameter but I can't pass a request parameter from a class view.
I have seen ways to do it in the internet but I can't understand it.
my models.py file
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return (self.name)
def get_absolute_url(self):
return reverse("home")
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField(max_length=3500)
category = models.CharField(max_length=255, default="uncategorized")
views.py
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
Thank you.
You can use self.request.user inside methods of class-based views; as an example:
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
def get_context_data(self):
current_loggedin_user = self.request.user
# ...
Edit (just print the username):
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
def __init__(self, *args, **kwargs):
print(self.request.user, self.request.user.username) # print user & username
return super().__init__(*args, **kwargs)
I am trying to have a logged in User fill out a form to create a Group. On Group creation, I need the User to automatically be added to the Group.
For this problem, we are working with two models - User and Group.
User is the default model provided by Django.
Group is defined like so:
class Group(models.Model):
name = models.CharField(max_length=255, unique=True)
admins = models.ManyToManyField(User, default=1, related_name='user_username')
all_users = models.ManyToManyField(User, default=1)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('home')
def get_admins(self):
return ", ".join([u.username for u in self.admins.all()])
def add_admin(self, user):
self.admins.add(user)
def get_all_users(self):
return ", ".join([u.username for u in self.all_users.all()])
def add_user(self, user):
self.all_users.add(user)
self.save()
def is_admin(self, user):
if user in self.admins.all():
return True
else:
return False
And the view I'm trying to refactor is:
#login_required
def user_generated_group(request):
if request.method == 'POST':
form = GroupForm(request.POST)
user = request.user
if form.is_valid():
group = Group.objects.create(name=form.cleaned_data['name'])
group.add_admin(user)
group.add_user(user)
group.save()
return HttpResponseRedirect(reverse('home'))
else:
form = GroupForm()
context = {
'form': form,
'type': 'group',
'sidebar': Sidebar(request),
}
return render(request, 'form.html', context)
Here is the GroupForm:
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = '__all__'
exclude = ['all_users', 'admins', ]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'class': 'input'})
The goal is to utilize Django's built-in CreateView. The refactored view so far looks like:
class CreateGroup(LoginRequiredMixin, CreateView):
model = Group
form_class = GroupForm
template_name = 'form.html'
I have yet to implement the add_user and add_admin logic to this view. The hope is that I can manage these methods elsewhere.
I'm unsure of where to go from here. Should this logic be handled by the User or the Group?
If by the Group, should I be using a Manager?
If by the User, should I create a custom User model?
You can override the form_valid [Django-doc] method, to add the self.request.user to the group, like:
from django.http import HttpResponseRedirect
class CreateGroup(LoginRequiredMixin, CreateView):
model = Group
form_class = GroupForm
template_name = 'form.html'
def form_valid(self, form):
self.object = group = form.save()
group.all_users.add(self.request.user)
return HttpResponseRedirect(self.get_success_url())
I recently was able to figure out how to do this in the CreateView, but the same is not working for the UpdateView (Here's the original post on how to do it in the CreateView: (Django) Limited ForeignKey choices by Current User)
Essentially, I need it to display only the Universes created by the currently logged in user, but by default, it displays all universes.
When I try to set a form_class and have it mimic the solution for CreatView, it spits out an improperly configured error.
models.py:
class Universe(models.Model):
user = models.ForeignKey(User,related_name='universe',on_delete=models.CASCADE)
name = models.CharField(max_length=100, unique=True)
description = models.TextField(max_length=2000,blank=True,default="")
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('universe:singleuniverse',kwargs={'pk': self.pk})
class Meta:
ordering = ['name']
unique_together = ['user','name']
class Character(models.Model):
user = models.ForeignKey(User,related_name='characters',on_delete=models.CASCADE)
universe = models.ForeignKey("story_universe.Universe", on_delete=models.CASCADE)
name = models.CharField(max_length=255,unique=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('character_developer:singlecharacter',kwargs={'pk': self.pk})
class Meta():
ordering = ['name']
unique_together=['user','name']
views.py:
class UpdateCharacter(LoginRequiredMixin,generic.UpdateView):
model = Character
fields = ('universe','name')
template_name = 'character_developer/character_update_form.html'
UPDATE
The error was:
Error: ImproperlyConfigured at /characters/update/3/
UpdateCharacter is missing a QuerySet. Define UpdateCharacter.model, UpdateCharacter.queryset, or override UpdateCharacter.get_queryset().
and here's what the code looked like to get the error:
views.py:
class UpdateCharacter(LoginRequiredMixin,generic.UpdateView):
template_name = 'character_developer/character_update_form.html'
form_class = UpdateForm
def get_form_kwargs(self):
kwargs = super(UpdateCharacter,self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
forms.py
class UpdateForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
user = kwargs.pop('user')
super(UpdateForm,self).__init__(*args,**kwargs)
self.fields['universe'].queryset = Universe.objects.filter(user=user)
class Meta:
model = Character
fields = ('universe','name')
I believe you need the following in your views.py (almost an exact extension of your CreateCharacter):
class UpdateCharacter(LoginRequiredMixin, generic.UpdateView):
model = Character
template_name ='character_developer/character_create.html'
form_class = UpdateForm
def get_form_kwargs(self):
kwargs = super(UpdateCharacter,self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self,form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save
return super().form_valid(form)
I would caveat the above - make sure that the user instance in the request object is that of the currently logged in user, and has sufficient permission to update their own user - and can't somehow make a request on their behalf.
I'm practicing Django's generic views, particularly ModelForms
These are my views and models
models.py
class Post(models.Model):
user = models.ForeignKey(User)
post_title = models.CharField(max_length=200)
post_content = models.CharField(max_length=500)
post_date = models.DateTimeField('date posted')
def get_absolute_url(self):
return reverse('user-detail', kwargs={'pk': self.pk})
def __unicode__(self):
return self.post_title
forms.py
class PostForm(forms.ModelForm):
post_title = forms.CharField(
label=u'Title',
widget=forms.TextInput(attrs={'size':64})
)
post_content = forms.CharField(
label=u'Content',
widget=forms.TextInput(attrs={'size':128})
)
class Meta:
model = Post
views.py
class PostCreate(CreateView):
fields = ['post_title', 'post_content']
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(PostCreate, self).dispatch(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)
The view seems to work fine, not displaying any errors. However, when I check the Django admin page after submitting a form and saving a post, the post is not saved into the database for some reason.
Any idea why this is happening?
As I can see from your code you use
self.object = form.save(commit=False)
which mean that object will not be saved to database, but you can use it for futher processing. So you should use something like this:
self.object = form.save(commit=False) # Not hit database
self.object.user = self.request.user # Update user
self.object.post_date = datetime.now() # Update post_date
self.object.save() # And finally save your object to database.
Try this.
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)