I Have a very strange phenomenon
In my app the user Create a project and is redirected to that project detail using its pk. On that project detail page he is asked to create a team and when the team is created he is redirected again to the project detail page but to the wrong pk
for example: I just created a project and I got redirected to .../project/24. I was asked to create a team, I created it but got redirected to ../project/17 any idea why and how to redirect my page to the right URL ?
model.py:
class Team(models.Model):
team_name = models.CharField(max_length=100, default = '')
team_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
def __str__(self):
return self.team_name
class TeamMember(models.Model):
user = models.ForeignKey(MyUser)
team = models.ForeignKey(Team)
def __str__(self):
return self.user.first_name
class Project(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
def __str__(self):
return self.name
views.py
class TeamCreate(CreateView):
model = Team
fields = ['team_name']
template_name = 'team_form.html'
def form_valid(self, form):
valid = super(TeamCreate, self).form_valid(form)
form.instance.team_hr_admin = self.request.user
obj = form.save()
#SELECT * FROM project WHERE user = 'current_user' AND team_id = NULL
obj2 = Project.objects.get(project_hr_admin=self.request.user, team_id=None)
obj2.team_id = obj
obj2.save()
return valid
return super(TeamCreate, self).form_valid(form)
def get_success_url(self):
project = Project.objects.get(team_id=self.obj, project_hr_admin=self.request.user)
return project.get_absolute_url()
The problem here is your CreateView is refering to a TeamObject and not project.
You should override the get_success_url method:
def get_success_url(self):
project = Porject.objects.get(team_id=self.object, project_hr_admin=self.request.user)
return project.get_absolute_url()
The function called was the get_absolute_url of your Team model. So you're calling the project detail view but with the team pk => you get a random project assuming there's a project with a pk which has the same value as your project or, the pk you're sending doesn't exist and you'll have a 404 error (pk doesn't exist).
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
That's the one in your Team model, but you call ProjectDetails. So here, self.pk is Teaminstance.pk.
What I do in the code i gave you is to call the get_absolute_url of the project instance.
But as told in the other answer, you should remove or change your get_absolute_url of your team model.
class Team(models.Model):
# ...
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
^^^^^^^
Here, the wrong pk will be delived. Thx to #Bestasttung for clarification
Related
Setup
I have two models in my app. One is for a Journal and the other one is for entries to that journal. I wrote a CreateView class that will allow user to create a Journal entry for any Journal id currently located in. Ideally I want the class to "refresh" with the updated entry or in other words the get_success_url should lead the page we are currently located at.
views.py
class ToJournalEntriesList(LoginRequiredMixin, CreateView):
model = to_journal_entry
template_name = 'to_journals/to_journal_entries_list.html'
fields = ('body',)
def get_success_url(self):
return reverse('to-journals', kwargs={'slug':self.object.slug})
def form_valid(self, form):
current_journal = to_journal.objects.get(journal_user=self.request.user, slug=self.kwargs['slug'])
form.instance.journal_user = self.request.user
form.instance.journal_name = current_journal
return super(ToJournalEntriesList, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(ToJournalEntriesList, self).get_context_data(**kwargs)
context['to_journal_entries'] = to_journal_entry.objects.all()
return context
models.py
class to_journal(models.Model):
journal_name = models.CharField(max_length = 40)
slug = AutoSlugField(populate_from='journal_name')
date_created = models.DateTimeField(auto_now_add=True)
journal_user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return str(self.journal_user) + " " + self.journal_name
def get_absolute_url(self):
return reverse('to-journals')
class to_journal_entry(models.Model):
body = models.TextField()
entry_date = models.DateTimeField(auto_now_add=True)
journal_name = models.ForeignKey(to_journal, on_delete=models.CASCADE,)
journal_user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return str(self.journal_name) + " " + str(self.entry_date)
def get_absolute_url(self):
return reverse('to-journal-entries', args=(self.slug))
urls.py
urlpatterns = [
path('', CreateToJournal.as_view(), name='to-journals'),
path('<slug:slug>', ToJournalEntriesList.as_view(), name='to-journal-entries'),
]
Error
With the current setup that I have I get:
Which makes sense, because the to_journal_entry model does not have that that field.
Question
I am sure both my get_success_url() in views.py and get_absolute_url() in models.py are done incorrectly, but I could not find a good explanation of how those work. How should I set them up to achieve desired result? Thanks a ton in advance!
I appreciate everyone taking a look. Best, Rasul.
You can just follow the relationship:
def get_success_url(self):
return reverse('to-journals', kwargs={'slug': self.object.journal_name.slug})
Your self.object is a to_journal_entry. You probably want to use the to_journal, you can do that by obtaining the journal_name:
class ToJournalEntriesList(LoginRequiredMixin, CreateView):
model = to_journal_entry
template_name = 'to_journals/to_journal_entries_list.html'
fields = ('body',)
def get_success_url(self):
return reverse('to-journals', kwargs={ 'slug': self.object.journal_name.slug })
Note: usually the names of the models are written in PerlCase, so JournalEntry instead of to_journal_entry.
I want to save the Portfolio products details in PortfolioProducts model in django
I have models like below:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
Portfolio form:
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
fields = ['name']
My view file:
def edit(request):
portfolio_form = PortfolioForm
if request.method=="POST":
portfolio_id=request.POST.get('portfolio_id')
portfolio_detail = Portfolio.objects.get(pk=portfolio_id)
pform = portfolio_form(request.POST, instance=portfolio_detail)
if pform.is_valid():
portfolio = pform.save(commit = False)
portfolio.save()
products=request.POST.getlist('product_id[]')
for product in products:
ppform = PortfolioProducts(product_id=product, portfolio_id=portfolio_id)
port_product = ppform.save()
I am trying to save and update the Portfolio products like this, but is adding products to portfolio multiple time.
Well, you don't need to update PortfolioProduct for updating Portofilio. Because even if you update Portfolio, its primary key remains same as before. So the relationship remains the same.
But, in your case, if PortofolioProduct does not exist for a product in products and Portfolio object, then you can create one like this:
for product in products:
ppform, _ = PortfolioProducts.objects.get_or_create(product_id=product, portfolio_id=portfolio_id)
Update
From comments: you need to either remove def save(self): methods from you Model(Because you are not doing anything particular in those save methods) or if intend to keep you save() methods, then you need to call the super properly, like this:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Portfolio, self).save(*args, **kwargs)
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
def save(self, *args, **kwargs):
super(PortfolioProducts, self).save(*args, **kwargs)
Yes, I also got stuck with the same issue in my django project. The thing it does in my case was everytime the user tries to update his/her profile, it created a new one, this is because of the Foreign Key to it. I fixed the issue by deleting the previous user profile (in your case it's portfolio) every time the user updates it.
class UserEdit(TemplateView):
template_name = 'accounts/homee.html'
def get(self, request):
form = UserProfilee()
ppp = UserProfile.objects.get(user=request.user)
return render(request, self.template_name, {'form': form, 'ppp': ppp})
def post(self, request):
form = UserProfilee(request.POST, request.FILES)
pppp = UserProfile.objects.get(user=request.user)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
if not post.image:
post.image = pppp.image
UserProfile.objects.filter(user=post.user).delete()
post.save()
return redirect('/home/homepage/')
args = {'form': form}
return render(request, self.template_name, args)
As you see,I filter the user and delete the user profile whenever user updates his/her profile thus leaving only 1 user profile.
I am having an error No MyUser matches the given query, and I am not sure why there is no a match since the URL is properly set.
In my app I have two kind of users: Employees and candidates that are both part of the model MyUser and are differentiated by a boolean is_candidate or is_employee.
My issue is when creating the user detail view.
I started with the EmployeeDetailView :
class EmployeeDetailView(LoginRequiredMixin, generic.DetailView):
#import pdb; pdb.set_trace()
model = MyUser
template_name = 'employee_details.html'
def get_object(self, queryset=None):
return get_object_or_404(MyUser, pk=self.kwargs['pk2'], members__project=self.kwargs['pk1'])
def get_context_data(self, **kwargs):
context = super(EmployeeDetailView, self).get_context_data(**kwargs)
employee_name = MyUser.objects.get(id=self.kwargs['pk2'])
team_list = Project.objects.get(id=self.kwargs['pk1']).team_id.members.all()
team_list_pop = Project.objects.get(id=self.kwargs['pk1']).team_id.members.all().exclude(id=self.kwargs['pk2'])
context={
'employee_name' : employee_name,
'team_list' : team_list,
'team_list_pop' : team_list_pop,
}
return context
on URL : url(r'^project/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.EmployeeDetailView.as_view(), name='EmployeeDetails'),
That work properly and I am able to access my user detail
So I wanted to do the same for CandidateDetailView with the code :
class CandidateDetailView(LoginRequiredMixin, generic.DetailView):
#import pdb; pdb.set_trace()
model = MyUser
template_name = 'employee_details.html'
def get_object(self, queryset=None):
print(self.kwargs['pk2'])
return get_object_or_404(MyUser, pk=self.kwargs['pk2'], applicant__project=self.kwargs['pk1'])
def get_context_data(self, **kwargs):
context = super(CandidateDetailView, self).get_context_data(**kwargs)
context={
}
return context
On URL : url(r'^project/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.CandidateDetailView.as_view(), name='CandidateDetails'),
But this time I get the error that there is no matching
Raised by: website.views.EmployeeDetailView
My model:
class Project(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey('registration.MyUser', blank=True, null=True)
candidat_answers = models.ManyToManyField('survey.response')
applicant = models.ManyToManyField(MyUser, related_name="applicant")
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk1' : self.pk})
class Team(models.Model):
team_name = models.CharField(max_length=100, default = '')
team_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
members = models.ManyToManyField(MyUser, related_name="members")
def __str__(self):
return self.team_name
What am I doing wrong ?
I am getting the error on the URL :http://127.0.0.1:8000/website/project/54/43/ where 54 is Project ID and 43 is User ID both exist and User ID 43 is part of project 54
url(r'^project/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.EmployeeDetailView.as_view(), name='EmployeeDetails'),
url(r'^project/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.CandidateDetailView.as_view(), name='CandidateDetails')
Both of these URL patterns have the same regex. If the EmployeeDetails URL pattern comes first, then it will always be used.
You need to change the regex for the views, so that they do not clash, for example:
url(r'^project/employee/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.EmployeeDetailView.as_view(), name='EmployeeDetails'),
url(r'^project/candidate/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.CandidateDetailView.as_view(), name='CandidateDetails')
I can't work out how to get the correct instance for the form_valid part of my generic view.
I am trying to allow a user to post on their project wall(bit like Facebook). I need the post to be related to an individual project(a user can have more than one project). Should the instance be a pk or the project title? Any example code or help would be very appreciated! I struggle understanding how when you create a new post, it knows which project to associate itself with.
views
class NewPost(CreateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/new_post.html'
def form_valid(self, form):
newpost = form.save(commit=False)
form.instance.user = self.request.user
newpost.save()
self.object = newpost
return super(NewPost, self).form_valid(form)
def get_success_url(self):
project_username = self.request.user.username
project_slug = self.object.slug
return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})
models
class UserProject(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100)
project_overview = models.CharField(max_length=1000)
project_picture = models.ImageField(upload_to='project_images', blank=True)
date_created = models.DateTimeField(auto_now_add=True)
project_views = models.IntegerField(default=0)
project_likes = models.IntegerField(default=0)
project_followers = models.IntegerField(default=0)
slug = models.SlugField(max_length=100, unique=True) #should this be unique or not?
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(UserProject, self).save(*args, **kwargs)
def __unicode__(self):
return self.title
class ProjectPost(models.Model):
project = models.ForeignKey(UserProject)
title = models.CharField(max_length=100)
post_overview = models.CharField(max_length=1000)
date_created = models.DateTimeField(auto_now_add=True)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
forms
#form to add project details
class UserProjectForm(forms.ModelForm):
class Meta:
model = UserProject
fields = ('title', 'project_picture', 'project_overview')
#form to create a post
class ProjectPostForm(forms.ModelForm):
class Meta:
model = ProjectPost
fields = ('title', 'post_overview')
Ok, in that case, I would recommend a URL something like
url(r'^(?P<pk>\d+)/post/add/$', views.NewPostCreateView.as_view(), name='...'),
and then a view like
class NewPost(CreateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/new_post.html'
def form_valid(self, form):
self.object = form.save(commit=False)
# Find project by using the 'pk' in the URL
project = get_object_or_404(UserProject, pk=self.kwargs['pk'])
# Then just set the project on the newPost and save()
self.object.project = project
self.object.save()
return super(NewPost, self).form_valid(form)
def get_success_url(self):
# Unchanged ...
I see in your code that you were trying to do something with the user but I don't understand why your Post does not have a user field (you may want to add a created_by) and the UserProject should already have a user set.
I am also assuming the user got to the his/her project first, so you know by definition that the project he is adding a post to is his. If that is not the case, then just change the logic to get the UserProject through a regular query. e.g. maybe with `UserProject.objects.get(user = self.request.user) if there is one project per user (again, just as an example).
Anyway, I am making some assumptions here, but hopefully the main question was how to set the project on the newPost and that is answered in my example.
I am trying to create two user types in django 1.3. I am subclassing the AUTH_PROFILE_MODULE with the following models.py:
class Member(models.Model):
ROLE_CHOICES = (
(0, 'Guide'),
(1, 'Operator'),
)
user = models.ForeignKey(User, unique=True)
location = models.CharField(max_length=60)
role = models.IntegerField(choices=ROLE_CHOICES)
class Guide(Member):
bio = models.TextField(blank=True)
experience = models.TextField(blank=True)
image = models.ImageField(blank=True, upload_to='images')
fileupload = models.FileField(blank=True, upload_to='files')
def __unicode__(self):
return self.user.username
def get_absolute_url(self):
return '/profiles/guides/%s' % self.user.username
class Operator(Member):
bio = models.TextField(blank=True)
image = models.ImageField(blank=True, upload_to='images')
def __unicode__(self):
return self.user.username
def get_absolute_url(self):
return '/profiles/operators/%s' % self.user.username
I am using generic class based views and can get the ListView to work for the Guide and Operator models I cannot get the DetailView to work. My views.py is as follows:
class GuideDetailView(DetailView):
model = Guide
context_object_name = 'guide'
template_name = 'members/guide_detail.html'
class GuideListView(ListView):
model = Guide
context_object_name = 'guides'
template_name = 'members/guide_list.html'
Any idea what might be missing?
Either provide a queryset:
class GuideDetailView(DetailView):
queryset = Guide.objects.all()
or override the get Method of DetailView:
class GuideDetailView(DetailView):
def get(self):
return "Everything you want, maybe: Guide.object.get(id=1)"
Given this in your urls.py:
url(r'^(?P<my_id>\d)/$', GuideDetailView.as_view(),),
You need to override get, like this:
class GuideDetailView(DetailView):
def get(self, request, **kwargs):
# lookup Guide Id in your database and assign it object
self.object = Guide.objects.get(pk=kwargs.get('my_id'))
# add object to your context_data, so that you can access via your template
context = self.get_context_data(object=self.object)
return self.render_to_response(context)