I am new to Django I have two models are user and address, here user having two
foreign key fields are 'localaddress', 'permanentaddress'
Address model:
class Address(models.Model):
fulladdress = models.CharField(max_length=1000, null=True, blank=True)
additional_address = models.CharField(max_length=1000, null=True, blank=True)
street_address = models.CharField(max_length=150, null=True, blank=True)
route = models.CharField(max_length=150, null=True, blank=True)
city = models.CharField(max_length=100, null=True, blank=True)
state = models.CharField(max_length=100, null=True, blank=True)
country = models.CharField(max_length=100, null=True, blank=True)
pincode = models.IntegerField(null=True, blank=True)
class Meta:
db_table = 'address'
User model:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
localaddress = models.ForeignKey(Address, on_delete=models.CASCADE, related_name="localaddress", null=True, blank=True)
permanentaddress = models.ForeignKey(Address, on_delete=models.CASCADE, related_name="permanentaddress", null=True, blank=True)
class Meta:
db_table = 'user'
settings.py:
AUTH_USER_MODEL = 'student.User'# changes built-in user model to ours
here both localaddress and permanentaddress having same foreign key (Address model only)
Edit form:
forms.py:
class LocaladdressForm(forms.ModelForm):
class Meta:
model = Address
fields = ['fulladdress', 'additional_address', 'street_address', 'route', 'city', 'state', 'country', 'pincode']
def save(self, commit=True):
user = super(LocaladdressForm, self).save(commit=False)
user.fulladdress = self.cleaned_data['fulladdress']
user.additional_address = self.cleaned_data['additional_address']
user.street_address = self.cleaned_data['street_address']
user.route = self.cleaned_data['route']
user.city = self.cleaned_data['city']
user.state = self.cleaned_data['state']
user.pincode = self.cleaned_data['pincode']
user.country = self.cleaned_data['country']
if commit:
user.save()
return user
views.py:
def address_form(request):
if request.method == 'POST':
address = Address()
form = AddressForm(request.POST, instance=address)
if form.is_valid():
form.save()
userid = request.user.id
User.objects.filter(pk=userid).update(localaddress=address)
return redirect(reverse('student:view_profile'))
else:
args = {'form': form}
return render(request, 'student/addressform.html', args)
else:
form = AddressForm()
args = {'form': form}
return render(request, 'student/addressform.html', args)
i am using for loop to render form elements, i don't know where i did wrong
addressform.html:
<form method="post">
{% csrf_token %}
{{ form.fulladdress }}
{{ form.additional_address}}
{{ form.street_address}}
{{ form.street_address }}
{{ form.city }}
{{ form.state }}
{{ form.pincode }}
{{ form.country }}
</form>
Here i need to render one form localaddress and another form permanentaddress foreign key fields to my template. Initially i am trying first form (localaddress) please help me any one.
Thanks in advance ...
Localaddress form screenshot: Here i did it Autocomplete Address Form using google address api reference link here Autocomplete Address
I am able to persist address and user object but user object creating new object it is not persisting existing object (means localaddress)
I can answer partially about a concept in Django.
Whenever you create a relationship between two Django models you decide which model will be the main model. Over here you have User model and Address model. I am sure in most cases you will agree that User model is the main model and Address will be the sub-model or child model or whatever you call it.
So your ForeignKey field should ALWAYS be on the sub-model. So instead of using ForeignKey on User model like you have done you should do something like this:
address_type_choices = [
(1, 'Local'),
(2, 'Permanent'),
]
class Address(models.Model):
user = models.ForeignKey(User)
type = models.IntegerField(default=1, choices=address_type_choices)
house = model.CharField(max_length=50)
road = model.CharField(max_length=50)
area = model.CharField(max_length=50)
...
Please change the structure of your model and edit the question. I am sure that is what others will suggest too.
Remember, main model will not have foreign key field.
Update: Changed the model to allow you to maintain both local and permanent address.
Related
I'm trying to add a commenting and replying system to my products model but I can't add replies to comment.
This is being done in the same page where the product details are being shown to the user.
Edit:
I'm getting a Cannot assign "<Product: Test Product>": "Reply.comment" must be a "Comment" instance. error at new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
views.py:
class ProductFeedbackView(DetailView):
model = Product
template_name = 'store/product_feedback.html'
def get_context_data(self , **kwargs):
data = super().get_context_data(**kwargs)
connected_comments = Comment.objects.filter(product=self.get_object())
number_of_comments = connected_comments.count()
data['comments'] = connected_comments
data['no_of_comments'] = number_of_comments
data['comment_form'] = CommentForm()
connected_replies = Reply.objects.filter(comment=self.get_object())
number_of_replies = connected_replies.count()
data['replies'] = connected_replies
data['no_of_replies'] = number_of_replies
data['reply_form'] = ReplyForm()
return data
def post(self , request , *args , **kwargs):
if self.request.method == 'POST':
reply_form = ReplyForm(self.request.POST)
if reply_form.is_valid():
content = reply_form.cleaned_data['content']
new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
new_reply.save()
return redirect(self.request.path_info)
if self.request.method == 'POST':
comment_form = CommentForm(self.request.POST)
if comment_form.is_valid():
content = comment_form.cleaned_data['content']
new_comment = Comment(content=content, author=self.request.user, product=self.get_object())
new_comment.save()
return redirect(self.request.path_info)
models.py:
class Product(models.Model):
author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
title = models.CharField(max_length=120, unique=True)
description = models.CharField(max_length=300, blank=True, null=True)
class Comment(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)
content = models.CharField(max_length=200, null=True, blank=False)
class Reply(models.Model):
comment = models.ForeignKey(Comment, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)
content = models.TextField(null=True, blank=False)
As the error message suggests, you're trying to assign a Product instance to a field that expects a Comment instance.
This is the line where you try to do this:
connected_replies = Reply.objects.filter(comment=self.get_object())
self.get_object() returns a Product instance as you defined model = Product on your View.
To get the replies connected to your product, you will need to loop over all comments and per comment all its replies as you defined these relations as foreignkeys.
For example:
for comment in connected_comments:
comment_replies = Reply.objects.filter(comment=comment)
#Vincent answer is ok, the error is from wrong model passed to filter of Replay model.
But for remedy to make it easier in template for showing comments and replies to those comments i suggest delete from context
data['replies']
data['no_of_replies']
and in template where you loop through comments (just example):
{% for comment in comments %}
<h1>{{comment}}</h1>
{% for reply in comment.reply_set.all %}
<p>{{ reply }} </p>
{% endfor %}
{% endfor %}
use reverse relationship with reply_set.
Oh, and for optimization add prefetch_related to your query:
Comment.objects.filter(product=self.get_object()).prefetch_related('reply_set')
hi am working on a project where am using multiple user data a user did a post onto the site and when driver see that post he adds their offer to that post but when driver submit the post ...at the admin level the particular is selected automatically but the post is not selected on which he adds price this is my post model.py
class Loader_post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE ,related_name="Loader")
pick_up_station = models.CharField(max_length=150)
destination_station = models.CharField(max_length=150)
sender_name = models.CharField(max_length=150)
phone_number = PhoneNumberField(null=False, blank=False, unique=True)
receiver_name = models.CharField(max_length=150)
this is my second model of adding price to a particular post
class price(models.Model):
my_post = models.ForeignKey(Loader_post, related_name='prices')
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default='')
driver_price = models.CharField(max_length=150, null=True)
driver_name = models.CharField(max_length=150, null=True)
status = models.BooleanField(default=False)
this is my adding price to the post views.py
#login_required
def add_price_to_post(request, pk):
post = get_object_or_404(Loader_post, pk=pk)
user = request.user
if request.method == "POST":
form = price_form(request.POST)
if form.is_valid():
ps = form.save(commit=False)
ps.user = request.user
ps.status = True
ps.post = post
ps.save()
return redirect('Driver:Driverview')
else:
form = price_form()
return render(request, 'price_form.html', {'form': form})
this is my html add post button
{% for loader in Loader %}
this is loop and this is button
add price
you can see my_post is select automatically
In your model the field name is my_post while in the add_price_to_post you are adding ps.post. change that to ps.my_post.
I am trying to make a user panel in which each user's profile info (like avatar, joined date, etc.) are being displayed along with their posts. Here is the view that render the threads:
def topic(request, topic_id):
"""Listing of posts in a thread."""
posts = Post.objects.select_related('creator') \
.filter(topic=topic_id).order_by("created")
posts = mk_paginator(request, posts, DJANGO_SIMPLE_FORUM_REPLIES_PER_PAGE)
topic = Topic.objects.get(pk=topic_id)
topic.visits += 1
topic.save()
return render_to_response("myforum/topic.html", add_csrf(request, posts=posts, pk=topic_id,
topic=topic), context_instance=RequestContext(request))
The Topic model is:
class Topic(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(max_length=10000, null=True)
forum = models.ForeignKey(Forum)
created = models.DateTimeField()
creator = models.ForeignKey(User, blank=True, null=True)
visits = models.IntegerField(default = 0)
And the UserProfile model:
class UserProfile(models.Model):
username = models.OneToOneField(User)
name = models.CharField(max_length=30, blank=True)
city = models.CharField(max_length=30, blank=True)
country = models.CharField(
max_length=20, choices= COUTNRY_CHOICES, blank=True)
avatar = ImageWithThumbsField(), upload_to='images', sizes=((32,32),(150,150),(200,200)), blank=True)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, blank=True)
The problem is how best to join these two tables so that userprofile fields can be displayed in topic.html along with username?
Add them to context since you already have a database relation Users and Topics.
# add this to context
topic = Topic.objects.get(pk=topic_id)
creator = topic.creator.get().profile # This pulls the user object from creator field
context['creator'] = creator # Add to context
Now you can use the 'creator' context to pull fields
<h1>{{ creator.name }}</h1>
as for the avatar, if you have your media root set in settings you simply use an
<img src="/media/images/{{ creator.avatar }}">
Oh and also you can save alot of time by using ListView and DetailView part of Django's class based views.
Sorry forgot to mention you should add a related name to your one to one,
username = OneToOneField(User, related_name='profile')
I'm trying to follow this post to associate a profile picture to a user in Django.
I have the following model
class MyUser(AbstractBaseUser):
"""
Custom user class.
"""
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
email = models.EmailField('email address', unique=True, db_index=True)
is_staff = models.BooleanField('is staff', default=False)
first_name = models.TextField('first name', default=None, null=True)
last_name = models.TextField('last name', default=None, null=True)
date_of_birth = models.DateField('date of birth', null=True)
avatar = models.ImageField('profile picture', upload_to='static/media/images/avatars/', null=True, blank=True)
has_picture = models.BooleanField('has profile picture', default=False)
adult = models.BooleanField('is adult', default=False)
gender = models.CharField('gender', max_length=1, choices=GENDER_CHOICES)
objects = MyUserManager()
REQUIRED_FIELDS = ['date_of_birth', 'gender']
USERNAME_FIELD = 'email'
# Insert a lot of methods here
def set_avatar(self):
self.has_picture = True
I used the form from the post, but added this to my save() for the ChangeForm:
def save(self, commit=True):
user = super(MyChangeForm, self).save(commit=False)
if user.avatar: # If the form includes an avatar
user.set_avatar() # Use this bool to check in templates
if commit:
user.save()
return user
The logic behind this is to add a picture then set a bool flag to tell the template whether to display a generic "blank user" avatar if there's no picture associated with the profile and to display a thumbnail if there's an avatar attribute within the user.
From my form, neither the uploading nor the has_picture fields get set. Within admin, however, I can upload photos.
What am I doing wrong?
It's not good idea to set a Boolean for checking whether the user has an avatar. You have two options: you can play with the empty url in your template or define a method to set user avatar in models.py
Option 1: in your template
{% if user.avatar == None %}
<img src="DEFAULT_IMAGE" />
{% else %}
<img src="user.avatar"/>
{% endif %}
Option 2: in your models
def set_avatar(self):
_avatar = self.avatar
if not _avatar:
self.avatar="path/to/default/avatar.png"
Also, if your user never gets saved, if because you are calling save method with commit=False.
I'm looking for a way to dynamically add extra attributes to django model instances so that they are accessible in the template
For example,
in models.py
class Foo(models.Model):
name = models.TextField()
in views.py
def my_view(request):
f = Foo.objects.get(id=1234)
# method in question
f.____add_extra_attribute____('extra_attribute', attr_value)
return render(request, 'my_template.html', {'foo_instance':f})
in my_template.html
<h1>{{ foo_instance.name }}</h1>
<p>{{ foo_instance.extra_attribute }}</p>
is there a way to accomplish this without rendering the instance as a dictionary instead of a Foo model object?
Following #mgilson's comment, you can pass the extra_attribute to the template in a separate context variable:
def my_view(request):
f = Foo.objects.get(id=1234)
extra_attribute = attr_value
return render(request, 'my_template.html',
{'foo_instance': f, 'extra_attribute': attr_value})
If you still want to set the attribute on a model instance, you can set it directly:
f.extra_attribute = attr_value
Look at the example below. This is real code from my project. Here I have attached an attribute to the model object itself.
My model:
class Task(models.Model):
name = models.CharField(max_length=500)
description = models.TextField(max_length=500, blank=True)
assignee = models.ForeignKey(Employee, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
report = models.ForeignKey(UtilizationReport, on_delete=models.CASCADE, null=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE, null=True, blank=True)
billable_efforts = models.FloatField(
validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],
)
created_at = models.DateTimeField(default=timezone.now, editable=False)
reviewed = models.BooleanField(default=False)
In view.py:
task = Task.objects.get(id=1) # Just an example...won't be really using get with id
task_rate = BillingCatagoryRate.objects.get(rate_card=invoice.rate_card, billing_category=task.assignee.billing_category).billing_rate
task.task_price = task_rate * task.billable_efforts # Note: task_price is NOT a model field.
In the template:
<td class="text-center">{{ task.task_price }}</td>