when a user signs up, they get redirected to the homepage. If it is their first time logging in, I want to display a div, however if it is not their first time, I do not want this div to appear. Currently, I am relying on date_joined andlast_login to do this check.
However this only checks dates and not the time. Therefore the div will still appear until the date has changed. I only want the div displayed once. This is when the user first logs in.
Here is my code so far:
views.py:
def home(request):
context={}
user = request.user
if user.last_login.date() == user.date_joined.date():
context['isFirstTime'] = 'isFirstTime'
else:
pass
return render(request, 'home.html', context)
template:
{% if isFirstTime %}
<div style="width: 300px; height: 300px; background-color: green;">
</div>
{% endif %}
Does anybody know how I can alter this so it works with the current time and not the current date. This way the div is only displayed when a user first logs in. Anybody know a solution? Thank you. Also date_joined and last_login are datetime objects stored in the database.
Instead of using last_login and date_joined, consider creating a UserProfile model with an attribute is_first_login, you can read more about UserProfile here .
In your models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
is_first_login = models.BooleanField(default=True)
In your view:
def home(request):
context={}
user = request.user
if user.profile.is_first_login:
context['isFirstTime'] = 'isFirstTime'
user.profile.is_first_login = False
user.profile.is_first_login.save()
else:
pass
return render(request, 'home.html', context)
if (user.last_login - user.date_joined).total_seconds() < 5:
... # do your code
user.last_login = datetime.datetime.now()
user.save()
Both last_login and date_joined are DateTime instances so you need to compare them directly. But I recommend you to add some delta, like 5 seconds in example before to be sure
Related
I'm beginning to use Django, and I have some problems. I want to create new post such as blog. So I use views.py with model.py and forms.py.
but when I enter the create.html, I was writing what i want to post, and then click 'create' button. but it wasn't save in django object. I check in admin site, but there is no object. I think it means save object is failed. but I don't know where is the problem. plz help me T.T
in views.py
def create(request):
if request.method =="POST":
filled_form = ObForm(request.POST)
if filled_form.is_valid():
filled_form.save()
return redirect('index')
Ob_form = ObForm()
return render(request, 'create.html', {'Ob_form':Ob_form})
in create.html
<body>
<!-- form.py 모델 생성 -->
<form method="POST" action="">
{% csrf_token %}}
{{Ob_form.as_p}}
<input type="submit" value="확인" />
</form>
</body>
in models.py
from django.db import models
class Ob(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(null=True)
content = models.TextField(null=True)
update_at = models.DateTimeField(auto_now=True)
in forms.py
from django import forms
from .models import Ob
# 모델폼을 상속받아서 모델폼이 되었음
class ObForm(forms.ModelForm):
# 어떤 모델과 대응되는지 말해줌
class Meta:
model = Ob
fields = ( "title", "image", "content")
# 모델 폼 커스텀
# init - 내장함수 - (해당 클레스에 들어오는 여러가지 인자를 받을 수 있는 파라미터)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "제목"
self.fields['image'].label = "사진"
self.fields['content'].label = "자기소개서 내용"
self.fields['title'].widget.attrs.update({
'class': 'Ob_title',
'placeholder': '제목',
})
and urls.py
from django.urls import path
from .views import index, create, detail, delete, update
urlpatterns = [
path('', index, name="index"),
path('create/', create, name="create"),
path('detail/<int:Ob_id>', detail, name="detail"),
path('delete/<int:Ob_id>', delete, name="delete"),
path('update/<int:Ob_id>', update, name="update"),
]
A bit of guesswork here, but it's probably the case that filled_form.is_valid() is returning false, which will mean save() is never reached. To test this simply, just put an else and print on the if.
if filled_form.is_valid():
filled_form.save()
return redirect('index')
else:
print("Form validation failed")
print(filled_form.errors)
It's likely that you'd also want to return these errors to the user in the future, which means you'll need to make a couple of changes.
Right now, regardless of whether ObForm validates successfully, you are creating a new instance and passing that to the user. My typical approach would be to declare the form variable at the top of the function, and if it isn't already set when it comes to render the view, create a new instance of the form then. This way, if the form was populated by the user already (i.e. the request was a POST) then the errors will be returned with their input, instead of clearing their input (which is really annoying from a user's point of view!).
As a side note, I'm going to guess that you submitted a form with empty image and content fields, and were expecting that to be stored in your database? Try changing your field declarations to:
title = models.CharField(max_length=50)
image = models.ImageField(null=True, blank=True)
content = models.TextField(null=True, blank=True)
update_at = models.DateTimeField(auto_now=True)
null=True tells the database to allow null values, but blank=True tells the form validation to allow empty values.
Intro: I have a 3 models user, post, group. User is able to make posts however each post has to belong to a group. Users have to choose from the existing groups for their posts. Users cannot add, delete, update group's.
Furthermore:
Users can become a member of groups and when they click on a certain group. They see all the posts in that group.
What I want When Users come on the home page they see posts that were added since the last time they logged in
My Models
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
group = models.ForeignKey(Group, related_name='posts')
title = models.CharField(max_length=250, unique=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now=True)
My Views
class Homepage(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
context['object_list'] = Group.objects.filter(members=self.request.user)
#What am I doing wrong in the below code
new_posts = Post.objects.filter(created_at__gt=self.request.user.last_login).count()
context['new_posts'] = new_posts
else:
context['object_list'] = Group.objects.all()
return context
In my templates I have
<div class="list-group">
{% for group in object_list %}
{% if not new_posts %}
{{group.post.count}}
{% else %}
{{new_posts}}
{% endif %}
{% endfor %}
</div>
The Issue: Only the users who are already signed in and refresh their page see the new posts example:4 new, 3new etc... If a user signs in fresh after a new posts are created in a group. He does not see the 4 new, 3new . Instead it shows him just the number of posts in the group. not the new posts since he logged in. Why is this happening?
Well, its normal behavior because last_login stores datetime at which the user last logged in. When the user fresh logs in into the system, it stores the current time (timezone.now). So if a post is created before he is logged in, then it will not appear into the new_posts. So if you want to store the previous login/logout time, then you need to store it somewhere else(or make a new model field). You can try like this using user logged out signal:
from django.contrib.auth.signals import user_logged_out
def do_stuff(sender, user, request, **kwargs):
user.logout_time = timezone.now()
user.save()
user_logged_out.connect(do_stuff) # Hook an a method to user_logged_out signal to update logout time
And use it in the View like this:
last_post = Post.objects.last()
if last_post.created_at < request.user.last_login:
new_posts = Post.objects.filter(created_at__gt=self.request.user.logout_time).count()
context['new_posts'] = new_posts
Update
In this example, it is required to have a field to store logout time. If you have a CustomUser then you can directly create it in the CustomUser Model. Else, you can add it like this:
class Profile(models.Model):
user = models.OneToOneField(User)
logout_time = models.DateTimeField(default=timezone.now)
and store logout time like this:
user.profile.logout_time = timezone.now()
and filter New Posts Like this:
new_posts = Post.objects.filter(created_at__gt=self.request.user.profile.logout_time)
I have a site that when a user clicks a bootstrap glyphicon link they should be redirected to another page, this page is of the same glyphicon but in a green color to make it seem as if by pressing the link they activated the button. During this trasition I want the field active on my Profile to go from False to True. I have the following code:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
university = models.CharField(max_length=30, blank=True)
ROLE = (
('CUSTOMER', 'User'), # (value to be set on model, human readable value)
('WORKER', 'Worker'),
)
role = models.CharField(max_length = 20, choices = ROLE, default = 'USER')
active = models.BooleanField(default = False)
views.py
def active(request):
request.user.profile.active = True;
return render(request, 'core/customer_active.html', {'user': request.user})
home.html:
<a href="{% url 'active' %}"><span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span></href>
I am not sure why request.user.profile.active = True; does not update the state of the field, what will?
As others have said, you need to save. However, it is the profile you need to save, not the user, since that is a separate model.
profile = request.user.profile
profile.active = True
profile.save()
Is this a permanent change to the "active" property? You need to save the user object, if so. Like this:
def active(request):
request.user.profile.active = True;
request.user.save()
return render(request, 'core/customer_active.html', {'user': request.user})
Edit: might be worth noting that this isn't the smartest way to update a user's profile, by saving this attribute every time they hit this view, but if you're just wondering why the True value isn't persisting, this is the reason why.
I'm trying to create an application where users can send each other messages.
The function I am working on is called read, allows the user to read the message he receives.
The way my model works in the following manner: Every message is related to a thread and this will used to keep track of replied messages related to each other.
My function works by capturing the message id and filtering all the messages related to the message thread. Then I will populate a form with the current message id and allow the user to reply to the form.
When the user submits via POST, I will retrieve the hidden message id and create a new message using the retrieved message id thread.
The issue: I can't figure out how to raise an error for such situation when exceeding the character limit and populating the current message id with the raised error. Can someone kindly help me?
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=30, blank=True)
def __unicode__(self):
return self.user.username
class Thread(models.Model):
subject = models.CharField(max_length=100, blank=True)
user = models.ForeignKey(User)
class Message(models.Model):
user = models.ForeignKey(User, related_name='sender')
recipient = models.ForeignKey(User, related_name='recipient')
created = models.DateTimeField(auto_now_add=True)
body = models.CharField(max_length=1000)
read = models.BooleanField(default=False)
sentmessage = models.BooleanField(default=False)
thread = models.ForeignKey(Thread)
draft = models.BooleanField(default=False)
def __unicode__(self):
return self.body
Views:
#login_required
def read(request,id):
try:
messages = Message.objects.get(pk=id,recipient=request.user)
except Message.DoesNotExist:
return HttpResponseRedirect(reverse('world:Display'))
messages.read = True
messages.save()
if request.method =='POST':
form = ReplyForm(request.POST)
if form.is_valid():
id = request.POST.get('hidden_field', False)
try:
messages = Message.objects.get(pk=id ,recipient=request.user,sentmessage=True,draft=False)
except Message.DoesNotExist or Thread.DOesNotExist:
return HttpResponseRedirect(reverse('world:LoginRequest'))
person = Person.objects.get(user=messages.user)
if person.inbox == "D":
return HttpResponseRedirect(reverse('world:message'))
body = form.cleaned_data['body']
Message.objects.create(user=request.user,recipient=messages.user,body=body,thread=messages.thread,sentmessage=True,read=False)
return HttpResponseRedirect(reverse('world:message'))
message = Message.objects.filter(thread=messages.thread ).filter(created__lte=messages.created)
person = Person.objects.get(user=request.user)
initial = {}
initial.update({'hidden_field': messages.id})
form = ReplyForm(initial=initial)
return render(request,'read.html',{'messages':messages,'form':form,'message':message,'person':person})
forms
class ReplyForm(forms.Form):
body = forms.CharField(widget=forms.Textarea,required=False,max_length=555)
hidden_field = forms.CharField(widget=forms.HiddenInput())
Template:
<div class="box22">
{% for m in message %}
<div class="wrapper">
<div class="user">{{m.user.username}} </div>
<div class="message">{{m.body}}</div>
</div>
{% endfor %}
<form method="POST" >{% csrf_token %}
{{form.body}}{{form.hidden_field}}
<input type = "submit" value= "send" class="sen"/>
</form>
{{form.body.errors}}
First of all, I recommend you use only form.cleaned_data to get submited data.
id = form.cleaned_data['hidden_field']
Also, why you reassign id from request.POST data when you already have id defined in your function args? Maybe it should look like:
msg_response_id = form.cleaned_data['hidden_field']
Then, you always need to check hidden_field value and you can do it with your custom clean method. Add clean_hidden_field method to your form and also you should override init form method and pass id and msg_response_id.
In your views:
form = ReplyForm(initial=initial, id=id, msg_response_id=msg_response_id)
And forms:
class ReplyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.id = kwargs.pop('id', None)
self.msg_response_id = kwargs.pop('msg_response_id', None)
super(ReplyForm, self).__init__(*args, **kwargs)
hidden_field = forms.CharField(widget=forms.HiddenInput())
def clean_hidden_field(self):
if self.id and self.msg_response_id and self.id == self.msg_response_id:
# Raise form error, which will appear in your template.
raise forms.ValidationError(_('You can't reply to your own message.'))
The last paragraph describing what you want is not all too clear, but I believe what you are trying to do, is make an raise an exception when a certain requirement is not met in your form.
If that is the case, then simply catching an ValidationError which is inbuilt into django will do the trick.
So, something like this:
try:
form = ReplyForm(request.POST)
except ValidationError:
# do what you want here
Alternatively,
form = ReplyForm(request.POST)
if form.is_valid():
# do stuff
else:
raise ValidationError
If this does not work for you, then you might try validation the post-data without using django-forms, but usually django takes care of this problem for you, and it will automatically generate the error messages. You can take a deeper look # form validation here.
However if you want even more fine grain control you can make the form yourself, and handle the validation within your views. Otherwise, ValidationError should do the trick and let you redirect the error messages.
Following is the model which I have
class OrgStaff(BaseModel):
user = models.OneToOneField(User)
member_type = models.BooleanField(help_text="1. Read/Write 0. Read Only")
task = models.ForeignKey(ToDos, null=True, blank=True)
org = models.ForeignKey(Org)
# TODO Add possible activities
def __unicode__(self):
return self.user.username
Following is the forms file
class AddStaffForm(ModelForm):
class Meta:
model = OrgStaff
exclude = (
'task',
'org'
)
and this is how I process the view
if request.POST and form.is_valid():
form.save()
ret_url = reverse("some_view", kwargs={
'var':var,})
return redirect(ret_url)
return render(request, "form.html", {"form":form})
This form would render a dropdown, which will show all the users in the database, and a radio box.
But actually, I want to create the form, so that I can add a new user(first name, last name, username, email and password) and then the rest of the fields from the abvoe AddStaffForm.
So question basically boils down to adding fields of userform to the addstaffform.
And then handling them into the views.
Is it doable, or will I have to do it manually?
Can the above model form be extended so that I can first fill in a user detail, and then assign a type to him??
Please let me know, thanks.
Use two separate forms - UserForm, created out of models.User & AddStaffForm but exclude the 'user' field in the AddStaffForm. Use only ONE submit button for both.
So your template will look like:
<form method="post" action="/path/to/wherever">
{{ user_form }}
{{ add_staff_form }}
<button>Submit</button>
</form>
Then, when the user submits the form, process each form independently in the following order:
Process the user form first and save the user instance created by the form to the db. if user_form.is_valid() is True, you can do this by simply doing user = user_form.save()
Next, process the AddStaffForm but pass commit=False (i.e. staff = add_staff_form.save(commit=False) since it does not contain the value for the user field just yet. Provide the user values using staff.user = user and then staff.save()
Provided all other fields in the staff form are provided for (i.e. add_staff_form.is_valid() is otherwise True, this should result in the creation of a new staff instance written to db.
Hope this helps. :)