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')
Related
I am trying to save a form with the current logged in user's username, but the error "Cannot assign "'Neshno_Games2'": "League.host" must be a "Manager" instance." occurs
Views.py
class CreateLeaguesView(generic.CreateView):
model = League
template_name = "leagues/create-league.html"
form_class = LeaguesCreationForm
success_url = "/leagues/leagues"
def get_context_data(self, **kwargs):
context = super().get_context_data( **kwargs)
context['leagues'] = League.objects.all()
return context
def form_valid(self, form):
manager = self.request.user.username
League.objects.create(
host = manager,
)
return super(CreateLeaguesView, self).form_valid(form)
Model.py
class League(models.Model):
name = models.CharField(max_length=30)
no_players = models.IntegerField(default=20)
start_date = models.DateField(blank=False, null=False)
end_date = models.DateField(blank=False, null=False)
prize = models.CharField(max_length=300)
host = models.ForeignKey(Manager, on_delete=models.CASCADE)
def __str__(self):
return self.name
forms.py
class LeaguesCreationForm(forms.ModelForm):
class Meta:
model = League
fields = (
"name",
"no_players",
"start_date",
"end_date",
"prize",
)
You can try like this:
class CreateLeaguesView(generic.CreateView):
model = League
def form_valid(self, form):
form.instance.host= self.request.user.manager # accessing one to one data
return super().form_valid(form)
More information can be found here in this documentation: https://docs.djangoproject.com/en/4.0/topics/db/examples/one_to_one/
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 need to update my table every time a new value of "sku" is entered (not to create a new entry), but it does have to happen only if the "client" selected is the same. If the "client" is different, then the model should add a new object with the same "sku", but with different "clients".
I have tried to do the following in my models.py:
class ProductList(models.Model):
id_new = models.IntegerField(primary_key=True)
sku = models.CharField(primary_key=False, max_length=200)
client = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=256)
description = models.CharField(max_length=1000)
storage = models.CharField(max_length=256)
cost_price = models.CharField(max_length=256)
sell_price = models.CharField(max_length=256)
ncm = models.CharField(max_length=256)
inventory = models.IntegerField(null=True)
class Meta:
unique_together = (('sku', 'client'),)
But it is not working. How can I make that work?
You can try like this:
# form
class MyForm(forms.ModelForm):
class Meta:
model = ProductList
def save(self, *args, **kwargs:
client = self.cleaned_data.get('client') # get client from form cleaned_data
if hasattr(self.instance, 'pk') and self.instance.client != client: # check if client match's already existing instance's client
self.instance.pk = None # make a duplicate instance
self.instance.client = client # change the client
return super(MyForm, self).save(*args, **kwargs)
# views.py
# ...
def my_view(request, id):
instance = get_object_or_404(ProductList, id=id)
form = MyForm(request.POST or None, instance=instance)
if form.is_valid():
form.save()
return redirect('next_view')
return render(request, 'my_template.html', {'form': form})
Update
Um you can override the model as well. you can try like this:
# Untested Code but should work
def save(self, *args, **kwargs):
if self.pk:
current_instance = self.__class__.objects.get(pk=self.pk)
if current_instance.client != self.client:
self.pk = None
return super(ProductList, self).save(*args, **kwargs)
I want to have a URL path like this:
www.example.com/bachelor/frankfurt-university/corporate-finance
As you can see, the URL path includes 3 slugs in this example.
I have 3 different class for these categories. And slug is included inside each of them separately. What do I need to do in order to be able to achieve that type of URL paths? I can make URL path with a single slug but not with multiple slugs.
Every single help or thought is highly appreciated. I can't find any source for that.
URLS.py
app_name = 'mnsdirectory'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'), #views.index
path('study/<slug:studylevelslug>/<slug:subjectslug>/', views.SubjectDetailView.as_view(), name='subject-detail'),
path('study-abroad/<slug:studylevelslug>/', views.StudylevelDetailView.as_view(), name='studylevel-list'),
]
VIEWS.py
class IndexView(generic.ListView):
model = Programmesearch
template_name = 'mnsdirectory/index.html'
context_object_name = 'universities'
queryset = Programmesearch.objects.all()[:6]
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['studylevels'] = StudyLevel.objects.all()[:10]
return context
class SubjectDetailView(generic.DetailView):
model = Programmesearch
template_name = 'mnsdirectory/subject_detail.html'
slug_field = 'subjectslug'
slug_url_kwarg = 'subjectslug'
class StudylevelDetailView(generic.DetailView):
model = StudyLevel
template_name = 'mnsdirectory/study_level.html'
slug_field = 'studylevelslug'
slug_url_kwarg = 'studylevelslug'
MODELS.py
class Programmesearch(models.Model):
study_country = models.CharField(max_length=100,
choices=COUNTRY_CHOICE)
full_subject_name = models.CharField(max_length=100, blank=True,
null=True)
def get_unique_slug(self):
subjectslug = slugify(self)
unique_slug = subjectslug
counter = 1
while mnsdirectory.objects.filter(subjectslug = unique_slug).exists():
unique_slug = '{}-{}'.format(subjectslug, counter)
counter += 1
return unique_slug
subjectslug = models.SlugField(unique=True)
def __str__(self):
return self.subjectslug
def get_absolute_url(self):
return reverse('SubjectDetailView', args=[str(self.id)])
def pre_save_programmesearch_receiver(sender, instance, *args, **kwargs):
subjectslug = slugify(instance.full_subject_name)
exists = Programmesearch.objects.filter(subjectslug=subjectslug).exists()
if exists:
subjectslug = "%s-%s" %(subjectslug, instance.id)
instance.subjectslug = subjectslug
pre_save.connect(pre_save_programmesearch_receiver, sender=Programmesearch)
class StudyLevel(models.Model):
title = models.CharField(max_length=100, blank=True, null=False)
studylevelslug = models.SlugField(unique=True, editable=False, max_length=100)
def __str__(self):
return self.studylevelslug
def get_absolute_url(self):
return reverse('StudylevelDetailView', args=[str(self.id)])
def get_unique_slug(self):
studylevelslug = slugify(self.study_level)
unique_slug = studylevelslug
counter = 1
while StudyLevel.objects.filter(studylevelslug = unique_slug).exists():
unique_slug = '{}-{}'.format(studylevelslug, counter)
counter += 1
return unique_slug
def pre_save_studylevel_receiver(sender, instance, *args, **kwargs):
studylevelslug = slugify(instance.study_level)
exists = StudyLevel.objects.filter(studylevelslug=studylevelslug).exists()
if exists:
studylevelslug = "%s-%s" %(studylevelslug, instance.id)
instance.studylevelslug = studylevelslug
pre_save.connect(pre_save_studylevel_receiver, sender=StudyLevel)
I had the same problem, you can store and pass your first slug slug:studylevelslug(use session or save it as a fields in your model) then pass multi slug to your url path same as you did in your code :
path('study/<slug:studylevelslug>/<slug:subjectslug>/', views.SubjectDetailView.as_view(), name='subject-detail'),
in your template when you call {% url %} pass your slugs like this :
{% url 'subject-detail' studylevelslug=YOURFIRSTSLUG subjectslug=YOURSECONDSLUG %}
I used this in my project and it worked completely fine.
for more detail about how store your slug in session use this link:
How use session in Django
if you need any further help, ask and will happy to help.
A DetailView is for a single object so you cannot have it with 2 models.
Try something like this:
class SubjectDetailView(generic.DetailView):
model = Programmesearch
template_name = 'mnsdirectory/subject_detail.html'
slug_field = 'studylevelslug'
slug_url_kwarg = 'studylevelslug'
def get_study_level(self, *args, **kwargs):
return StudyLevel.objects.get(slug=subjectslug)
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
ctx['study_level'] = self.get_study_level()
return ctx
You'll now have access to object and study_level within your template. object will hold your Programmsearch data and study_level your StudyLevel data
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