I've got these two models.
class UserProfile(models.Model):
user = models.OneToOneField(User)
THIS SHOULD BE
def posts(self):
return self.user.poster.all()
INSTEAD OF
def posts(self):
return self.user.post_set.all()
def __unicode__(self):
return self.user.username
class Post(models.Model):
author = models.ForeignKey(User, related_name='poster')
def __unicode__(self):
return self.title
and I'm attempting to display all posts by a user on this html page, where user_list = UserProfile.objects.all()
{% for user in user_list %}
{{ user.user }}
{{ user.posts }}
{% endfor %}
However, I'm being greeted by this error page that says 'UserProfile' object has no attribute 'post_set', or 'UserProfile' object has no attribute 'post_set', depending on if I use user.user.posts or user.posts.
Anyone have any ideas?
Thanks
In your Post class you have defined the related name of your ForeignKey to User as poster. This means you can access all of a user's posts through user.poster.all().
So your the posts method of your UserProfile class should be as follows:
def posts(self):
return self.user.poster.all()
In you example User does not have post_set attribute. If you want to get all posts for related User try this:
self.user.post.all()
Related
models.py is :
class Todo(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,null=True,blank=True)
title=models.CharField(max_length=200)
desc=models.TextField(null=True,blank=True)
complete=models.BooleanField(default=False)
created=models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ['created']
views.py is:
class TaskCreate(generic.CreateView):
model = Todo
fields = '__all__'
template_name = 'create.html'
success_url = reverse_lazy('home')
create.html is:
<body>
go back
{{ form.as_p }}
<form method="post">
{% csrf_token %}
<input type="submit" value="submit">
</form>
</body>
Whenever I submit data from create.html form it doesn't save it to the database and throws this field is required on 'user' field. How do I resolve this?
You probably want to exclude the user field, since it is determined by the logged in user, so:
from django.conf import settings
class Todo(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, editable=False
)
# …
def __str__(self):
return self.title
class Meta:
ordering = ['created']
Then we inject the logged in user in the instance of the form:
from django.contrib.auth.mixins import LoginRequiredMixin
class TaskCreateView(LoginRequiredMixin, generic.CreateView):
model = Todo
fields = '__all__'
template_name = 'create.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
form.instance.user = request.user
return super().form_valid(form)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names.
Therefore you might consider renaming the view class to TaskCreateView, instead of TaskCreate.
I would like to be able to register different models from a single front view, as I can do from the admin create view.
for example in models.py:
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL,
null=True)
in admin.py I have:
#admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
in the admin panel I get
I would like to be able to add author by the same way from the create book view in frontend, thank you for your help
For this make create modelform for Book Model in forms.py
class AddBook(forms.ModelForm):
class Meta:
model = Book
fields = ['title','author']
set url for adding the book in urls.py
path('add/book/',views.add_book,name='add_book')
In views.py
def add_book(request):
if request.method == POST:
form = AddBook(request.POST)
if form.is_valid():
book = form.save(commit=False)
book.save()
return redirect('redirect where you want to redirect')
else:
form = AddBook()
return render (request,'add_book.html',{'form':form})
add_book.html
<form action= '{% url 'add_book' %} method='post'>
{% csrf_token %}
{{ form.as_p }}
<button type = 'submit'>Submit</button>
</form>
OR you can you use the built-in class-based generic views https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/
I'm creating a website that the user can look at other users profile but the problem is when the user enter another user profile it show his personal information
this is the urls.py file code
urlpatterns = [
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
]
this is the view.py file code
class UserPostListView(ListView):
model = Post = Profile
template_name = 'website/user_posts.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user)
def get_username_field(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Profile.objects.filter(user=user)
this is the models.py file
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField(verbose_name='Ålder', default=15,
blank=True)
def get_absolute_url(self):
return reverse('user_posts', kwargs={'pk': self.pk})
def __str__(self):
return f'{self.user.username} Profile'
user_posts.html file
{{ user.get_full_name }}
{{ user.profile.age }}
{{ view.kwargs.username }}
in the template it's show the username but it didnt' show the name and the age.
user is always the current logged-in user. Your view uses the Profile model, so you can either access profile or object.
{{ profile.user.get_full_name }}
{{ profile.age }}
Note, your get_username_field method is never called and does not do anything; you should remove it.
Note also, it's really not a good idea to store age as an integer in the database. That means you somehow have to update it every year, as people have a strange habit of getting older... Better to store the date of birth, and have a method to display the age.
First of all your get_username_field is of no use.
In your views.py,
class UserPostListView(ListView):
model = Profile
template_name = 'website/user_posts.html'
context_object_name = 'user_content'
allow_empty = False #this will show 404 if the username does not exists
def get_queryset(self):
return User.objects.filter(username=self.kwargs['username'])
# you can do it in one line now
Now to show this in html,
{% for user in user_content %}
{{user.get_full_name}}
# rest of your code
{% endfor %}
You can also show posts of that particular user in same way as above.
In my django form I am using a method to filter the drop down options to the ones that are related to the logged-in user. After the implementation the displayed values changed to objects rather than the __str__ value. I am posting the simplified codes and a snapshot that shows this. I have followed everything needed, but I cannot figure out why this is happening:
models.py
class Business(models.Model):
client=models.ForeignKey('Client',on_delete=models.CASCADE, limit_choices_to={'is_active':True},)
name=models.CharField(max_length=30,blank=False, unique=True,)
def __str__(self):
return self.name
class MMRequestAttributes(models.Model):
client=models.ForeignKey('Client',on_delete=models.CASCADE, limit_choices_to={'is_active':True},)
business=models.ForeignKey('Business', on_delete=models.CASCADE,limit_choices_to={'is_active':True},)
class Ticket(MMRequestAttributes):
no=models.CharField('Ticket Number',max_length=50,default=uuid.uuid4,null=False, blank=False, editable=False, unique=True)
subject=models.CharField('Subject',max_length=100,null=False, blank=False)
description=models.TextField('Description',max_length=500,null=True,blank=True)
created_at=models.DateTimeField('Created at',auto_now_add=True, editable=False)
updated_at=models.DateTimeField('Updated at',auto_now=True, editable=False)
created_by= models.ForeignKey(settings.AUTH_USER_MODEL)
status=StateField(editable=False)
def __str__(self):
return 'Ticket #' + str(self.pk)
views.py
def new_ticket(request):
form=NewTicket(request.user)
return render(request,'mmrapp/new_ticket.html',{'form':form})
admin.py
class UserExtend(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=False,null=False,)
client=models.ForeignKey('Client', on_delete=models.CASCADE,limit_choices_to={'is_active': True},)
forms.py
from django import forms
from .models import Ticket, Business
from .admin import UserExtend
from django.forms import ModelChoiceField
class NewTicket(forms.ModelForm):
def __init__(self,user, *args, **kwargs):
super(NewTicket, self).__init__(*args, **kwargs)
try:
client_id = UserExtend.objects.values_list('client_id', flat=True).get(user=user)
self.fields['business'].queryset=Business.objects.filter(client__id=client_id)
except UserExtend.DoesNotExist:
### there is not userextend corresponding to this user, do what you want
pass
class Meta:
model=Ticket
fields = ('subject','business')
new-ticket.html
{% extends 'mmrapp/__l_single_column.html' %}
{% load static %}
{% block main_col %}
<h1>New Ticket</h1>
<form method="POST" class="new-ticket">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Submit</button>
</form>
{% endblock main_col %}
Finally I found the problem. Somehow django cannot load the original models' string representation of their objects (__str__) when showing them as choices in the drop downs. I had to explicitly define them again inside the from model. The attribute is called label_from_instance. The good thing is this way one can display them different from what they are originally defined in the models.
so the monkey patch would be:
self.fields['business'].label_from_instance = self.business_label
#staticmethod
def business_label(self):
return str(self.name)
In Python 2.7 we were using
def unicode(self):
return self.name
After the python upgrade from 2.7 to 3.6 it was showing all object references in the dropdown so I added
def str(self):
return self.name
I added this method to the model in question:
def __str__(self):
return self.whatever_prop_suits_your_needs
I have a fairly simple DetailView:
class TrackDetails(DetailView):
model = Track
And in my urls.py:
url(r'^(?P<slug>[-\w]+)/$', TrackDetails.as_view(), name='track-details'),
The model:
class Track(models.Model):
....
# Variables
track_type_choices = [
('ORG', 'Original'),
('RMX', 'Remix'),
('CLB', 'Collab'),
('LIV', 'Live'),
]
# Model Fields
user = models.ForeignKey(User, unique=False)
title = models.CharField(max_length=100)
desc = models.TextField(max_length=7500)
track_type = models.CharField(max_length=3,
choices=track_type_choices,
default='ORG')
track_type_content = models.CharField(max_length=100,blank=True)
created = models.TimeField(auto_now=True,auto_now_add=False)
upload = models.FileField(upload_to=generate_user_folder_tracks,storage=OverwriteStorage(),validators=[is_mp3])
albumart = models.ImageField(upload_to=generate_user_folder_art,storage=OverwriteStorage(),validators=[is_square_png])
private = models.BooleanField(default=False)
downloadable = models.BooleanField(default=False)
likes = models.ManyToManyField(User, related_name="likes",blank=True)
dislikes = models.ManyToManyField(User, related_name="dislikes",blank=True)
plays = models.BigIntegerField(default=0)
slug = models.SlugField(max_length=50,unique=True)
The model displayed in the view has a "user" field connected to the user model, I want to use this in the url, so that instead of writing "www.domain.com/slug/" I would write "www.domain.com/user/slug" to access the view of the instance.
Additionally, I have extended the User model with a field called "Display_name", I'd like to show this field instead of the username in my template (track_detail.html):
{% include '__header.html' %}
{% load static from staticfiles %}
<div id="track_container">
<div id="track_titleinfo">
<div id="track_artist" class="text">{{ object.user }}</div>
<div id="track_title" class="text">{{ object.title }}</div>
{% if object.track_type == 'ORG' %}
{% else %}
<div id="track_type" class="text">({{object.track_type_content}})</div>
{% endif %}
</div>
</div>
{% include '__footer.html' %}
<img src="/static/users/{{ object.user }}/art/{{ object.slug }}.png" alt="">
The div with the ID "track_artist" displays the raw username (In this case, enitoni), I'd like it to display the display_name (In this case "Ekchö") from the userprofile class of the user who owns the Track instance:
class UserProfile(models.Model):
user = models.OneToOneField(User)
display_name = models.CharField(max_length=50, default="null")
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
To include the username in the detail view, you first need to add it to your url patterns.
url(r'^(?P<username>[-\w]+)/(?P<slug>[-\w]+)/$', TrackDetails.as_view(), name='track-details'),
Then, since you are using DetailView, you need to override get_object so that you use the username and slug to fetch the object.
from django.shortcuts imporg get_object_or_404
class TrackDetails(DetailView):
model = Track
def get_object(self, queryset=None):
return get_object_or_404(
Track,
user__username=self.kwargs['username'],
slug=self.kwargs['slug'],,
)
Displaying the display_name of the user in the template is a separate problem. If you have a user, you can follow the one to one key backwards to the profile with user.userprofile. Therefore, in your template you can show the display_name with.
{{ object.user.userprofile.display_name }}
To access username and slug first pass in the two keywords:
url(r'^/(?P<username>\d+)/(?P<slug>[-\w]+)/$', get_userpage, name='track-details'),
Then check if Track.objects.filter(slug=slug, username=username) returns anything:
def get_userpage(request, username, slug):
"""Render user page"""
user = User.objects.get(username=username)
track_song = Track.objects.filter(slug=trackslug, user=user).first()
if track_song:
# return song page for user
else:
# try to return user
track_user = Track.objects.filter(user=user).first()
if track_user:
# return user page
# if nothing returned above
# return 404
Previous suggestions:
you can you use get_object_or_404(Track, slug=slug) in your view to return the correct response.
you could also redirect a user to their unique combination of username and slug using:
redirect('track-username-slug', username=myusername slug=myslug, permanent=True)
where track-username-slug is your named url