Django - ManytoOne relations and list of data entered by user - python

I am new to django but really want to learn how to use this framework.
What want to do :
I have a form, that allows the user to create a new client for example.
So in this form I have 3 fields :
Name
Description
Skills
The 'Skills' field is currently a text area, where the user would copy and paste a list already existing in another document(excel). Therefore, each element is separated by a splitline.
What I would like to do is :
create in the database the client entered by the user
In the database, link the description entered by the user to the client
In the database, link each skill entered by the user to the name so that I can work on them independantly of each other in further functions/apps. I don't want all of the skills to be linked to the client as one big bloc.
So I read the documentation and thought about using a ForeignKey. The thing is I was unable to get an input text area when using a ForeignKey. Everytime I used it I got an empty 'select list' like this ...
Empty list field
And even though I would be able to get that, I still don't know how to treat each element separatly of the others..
This is the code I have now :
model.py
class Post(models.Model):
name = models.CharField(max_length=100, null=True)
description = models.TextField(null=True)
skills = models.TextField(null=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['name', 'description', 'skills']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
post_form.html
{% extends "taskflow/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Client</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Save</button>
</div>
</form>
</div>
{% endblock content %}
As an example of what I tried to implement using a Foreign Key :
models.py
class Skills(models.Model):
element = models.TextField(null=True)
class Post(models.Model):
name = models.CharField(max_length=100, null=True)
description = models.TextField(null=True)
skills = models.ForeignKey(Skills, on_delete=models.CASCADE)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['name', 'description', 'skills']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
If in views.py, instead of calling 'skills' I call 'element', I get an error saying 'element' is undefined.
I thought that using a ForeignKey would include the 'element' field contained in Skills in the 'skills' field contained in Post. So from what I understand it is not the case.
What am I doing wrong?
Any information and tips would be welcome.
Thanks a lot.

Displaying foreign key field
If you want to display skills.element in your view then you don't have to call skills.element in fields just define __str__() method in your Skills model.
class Skills(models.Model):
element = models.TextField(null=True)
def __str__(self):
return self.element
p.s. Regarding the relation between Post and Skill, I think it might be Many to Many because one Post can have many Skills and one Skill can belong to many Posts but your current model doesn't allow that (Any Post can have only one Skill while one Skill belong to many Post).
You can find out more about how to implement many to many relationship in Django at https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/

** I CHANGED SKILLS TO ADDRESS **
I thought about this :
Get a TextField instead of ForeignKey
the user enters his list like this for example :
TextField
Then in Views.py I try to parse it, by creating a new field and use splitlines() like this:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['name', 'description', 'address']
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.address_list = self.object.address.splitlines()
form.instance.author = self.request.user
self.object.save()
return super().form_valid(form)
Here I think i need to do something like
for element in self.object.address_list:
#create object here
But I don't really know how to do it and most of the time I get
Post object has no attribute object
And also, in my template if I use for example :
<p class="article-content">{{ post.address_list }}</p>
I get ['123456', '123457', '123458']
And if I use :
{{ post.address_list.1 }}
I'll get the first str character which is '
So the general idea would be to get from this form this output in the db :
db
as I want to create an instance of each address in the db, linked to the client.
Thanks for your very appreciated help !

Related

Reference ForeignKey in Django DetailView

I am struggling to reference my ForeignKey from a basic DetailView in Django.
The models.py I am using:
class Posts(models.model):
url = models.URLField()
class Comment(models.model):
post = models.ForeignKey(Posts, related_name='comments', on_delete=models.CASCADE)
content = models.CharField(max_length=500, blank=False)
views.py:
class PostDetailView(DetailView):
model = Posts
context_object_name = 'posts'
I am trying to reference the comments in my posts detail page.
posts_details.html:
{% for comment in posts.comments.all %}
{{comment.content}}
{% endfor %}
I have also tried changing posts.comments.all to posts.comments_set.all and still am getting no results.
I feel like it is something small that I am missing, but I can't figure it out.
The data is there, and it was input correctly with the foreign key reference, but I cannot reference it through the detail view.
Edit with answer:
I was able to get this to work fairly simply by adding this to the comment model:
def get_absolute-url(self):
return reverse('post_detail', kwargs={'pk': self.post.pk})
That allowed me to access the post in the post_detail.html with the following loop:
{% for comment in posts.comments.all %}
{{comment.content}}
{% endfor %}
class Posts(models.model):
url = models.URLField()
def get_all_comments(self):
return Comment.objects.filter(post=self.pk)
Use this method, add the returned queryset to the context.
Your Posts model has no comments field ... so posts.comments.all is always empty. Unfortunately you do not get an error message if you try to access non existing fields in template tag

one user viewing another users profile in django

I would like to implement a functionality where another user A clicks the picture of a different user B and is automatically redirected to the profile of user B. how do I do this? please look at my HTML where I stated something about a link
view.py
class profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
bio = models.TextField(blank=True)
def __str__(self):
return f'{self.user.username} profile'
html:
{% for post in posts %}
<div class="icl-NavigationList-item">
<div class="icl-NavigationList-link icl-NavigationList--primary">
<div class="icl-NavigationList-text">
<h2 class="icl-NavigationList-title">
<div class="upperText">
<h2 class="card-title"style="background-color:{{post.post_colours}};">{{post.job_title}}</h2>
<a class="a-tags" href="*{{ i need to link the post author's profile here}}*" data-tippy-content="#dribble_handle">
<img src="{{post.author.profile.profile_pic.url}}" style="border-radius: 100%;float:right;"
alt="author"width="30" height="30"></a></div>
<div class="lowerText"> <p class="card-text">{{post.currency}} {{post.salary}}</p>
<p class="card-text"> Posted on {{post.date_posted}} by {{post.author}} </p>
<br>
{% if user.is_authenticated %}
my model
class profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
bio = models.TextField(blank=True)
category = models.CharField(max_length= 1000, choices =
Select_category,default='other')
def get(self, request, *args, **kwargs):
username = self.kwargs['username']
user_profile = profile.objects.get(user__username=username)
gigs = Gig.objects.filter(user__username=username, status=True)
print(user_profile.values())
return render(request, 'blog/profile.html', {'user_profile': user_profile, 'gigs': gigs,'name': username})
Alright, here’s an edited answer. I took a look at your code, and you don’t have the post model on here, so this is just me trying to put some pieces together to try. Ultimately, our goal should be to have the href look like this:
href="{{ post.author.get_absolute_url }}"
Note here that it is NOT {{ post.author.profile.get_absolute_url }}. It probably seems counter-intuitive to leave out profile in the url, but remember that the username argument passed is part of the User model, not the profile model.
(Note: the url will definitely depend on what the model for your post looks like. If the author is a ForeignKey to the user model, use what I’m typing here. If it is a ForeignKey relating to the profile model, you would replace author with author.user)
If that doesn’t work, make sure your urls.py is set up correctly.
urls.py
...
path(‘URL_YOU_WANT/<username>/, views.VIEW_NAME, name=“NAME”),
...
(Note: keep “<username>” exactly like that. It’s how Django knows to expect the username argument passed by your href.)
If things still aren’t working, I would rewrite your views.py view to be clearer. There is a bit of redundancy and confusion with your views.py and models.py. Here’s how I would go about it:
models.py
class profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
bio = models.TextField(blank = True)
def __str__(self):
return f’{self.user.username} profile’
views.py
def user_profile(request, username):
user = User.objects.get(username = username)
user_profile = profile.objects.get(user = user)
gigs = Gig.objects.filter(user = user, status = True)
return render(request, ‘blog/profile.html’, {‘user_profile’: user_profile, ‘gigs’: gigs, ‘name’: username})
Let me know if this helps!

Displaying all the posts of user in Django

I want to get all posts that a user send. For example user: admin, it will show admin's posts. I write some code, but probably I made a mistake and I got an error.
The error that I get:
AttributeError at /index_page/
'WSGIRequest' object has no attribute 'models'
Here is my code:
views.py
def index_page(request):
logged_in_user = request.models.author
logged_in_user_posts = Post.objects.filter(author=user)
return render(request, 'blog/post_list.html', {'posts': logged_in_user_posts})
models.py
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = RichTextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
post_list.html
<div>
{% for post in posts %}
Username: {{ post.author.username }}
Post: {{ post.text }}
<br>
{% endfor %}
Where is my mistake?
The error already hints to the line:
logged_in_user = request.models.author
A request object has no models attribute. It has a .user attribute that specifies the logged in user, so you can use:
logged_in_user = request.user
There is another error: you use Post.objects.filter(user=user), but there is no user variable either, there is a logged_in_user variable. So you can fix the view with:
def index_page(request):
logged_in_user_posts = Post.objects.filter(author=request.user)
return render(request, 'blog/post_list.html', {'posts': logged_in_user_posts})
Extra notes:
since you need a user, it makes sense to decorate the function with the login_required decorator [Django-doc]; and
Since Django allows you to change the User model, it might be beneficial to use the standard way to refer to the user model [Django-doc]. If you later change your mind and implement another user model, the creating migrations will automatically let the relations refer to the new model.
The two comments above are not strictly necessary to let it work. But it is what I think are good practices when developing Django apps (although of course these can be a bit "opinion-based").
The issue is in your view, request object does not contain your models.The view should be like below,
def index_page(request):
logged_in_user = request.user
logged_in_user_posts = Post.objects.filter(author=logged_in_user)
return render(request, 'blog/post_list.html', {'posts': logged_in_user_posts})
I am answering a little bit late , But may be it help someone later . you can use class-based views . In your case
views.py :
class UserPostListView(ListView):
model = Post
template_name = 'app_name/template_name.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User,username=self.kwargs.get('username'))
return Post.objects.filter(author=user)
and in your urls.py :
path('user/<str:username>', views.UserPostListView.as_view(), name='user-posts'),
and then in your template :
{% block content%}
<h1> My files </h1>
{% for post in posts %}
{% endfor %}
{% endblock %}

How to create new issues without selecting 'project'(manually select project_id)?

I am doing my school project by using Django to create a task management web application. My responsibilities are to create 'issue tracker', something like 'StackOverflow', but I am still at the very early stage of it. So I used crispy form to let the user create their own new issues. Since we use 'project_id' and 'issue_id' as parameters to direct users to different pages, so I encountered this problem, users have to manually choose 'project' when they create a new issue. I do not know how to put the issue which created by the user under right project without having to choose 'project' manually.
form.py
from django import forms
from .models import Comment,Issue
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title','content','project','status')
class NewIssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title','content','project','status')
new_issue.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h1>Add New Issue </h1>
<form method="POST" class="Issue-form">{% csrf_token %}
{{form|crispy}}
<button type="submit" class="btn btn-success">Submit</button>
</form>
{% endblock %}
models.py
class Issue(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
project = models.ForeignKey(Project,on_delete=models.CASCADE)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
published = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=9, choices=STATUS_CHOICES, default='draft')
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Issue, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('project:issue_tracker:issue_detail', kwargs={'project_id':self.project_id, 'issue_id':self.id})
def __str__(self):
return self.title
urls.py
urlpatterns =[
path('',views.list_of_issue,name='list_of_issue'),
path('<int:issue_id>/',views.issue_detail,name='issue_detail'),
path('<int:issue_id>/comment',views.add_comment,name='add_comment'),
path('new_issue/',views.new_issue,name='new_issue'),
path('<int:issue_id>/edit_issue/',views.edit_issue,name='edit_issue'),
path('<int:issue_id>/delete_issue/',views.delete_issue,name='delete_issue'),
path('<int:issue_id>/delete', TemplateView.as_view(template_name="issue_tracker/issue/nice_delete.html"), name='success_deletion'),
]
You can set an initial value for the project field in the issue form. An explanation of how that can be done can be found here.
Since you are using project_id and issue_id as parameters, something similar to the following will solve your problem (I guess):
def new_issue(request, project_id, issue_id):
.
.
form = IssueForm(initial={'project': project_id})
.
.

Django one-to-one relationships in one form

I'm trying to make a registration form for a extension of django's user model. It has a two relationships, one to User and Address. The form needs to have all the fields from User, UserDetails and Address. But i'm struggling to get the correct view and form. Just having a ModelForm for UserDetails combined with FormView doesn't add the fields for User and Address.
class User(AbstractBaseUser, PermissionMixin):
email = models.EmailField(unique=True)
class UserDetails(model.Model):
date_of_birth = models.DateField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
address = models.OneToOneField(Address, on_delete=models.CASCADE)
class Address(model.Model):
field = models.CharField(max_length=100)
field2 = models.CharField(max_length=100)
The form and view look like this:
class UserRegistrationForm(ModelForm):
class Meta:
model = Orchestra
fields = '__all__'
class UserRegistrationView(FormView):
form_class = UserRegistrationForm
template_name = 'users/register.html'
<form action="" method="post">
{% csrf_token %}
{{ form.as_table }}
<input type="submit" value="submit">
</form>
You missed the unicode or str method declaration in model classes. You should always declare it.
Remember that str is for python 3.x, and unicode for python 2.x.
Here is an example for class Address and python 2:
def __unicode__(self):
return '%s %s' % (self.field1, self.field2)
I had to create different forms for each model. Then combine all the instances created from the forms. See this answer for more details

Categories

Resources