I am creating a web application that allows a user to create a todolist where they can add, delete, and edit todo list items (todos). I am using the django framework to do so and I have gotten it to work for the most part. So far, I have implemented adding, editing, and deleting todos from a todolist, I have created a superuser, and I am able to log in to the site using my superuser.
However, I want to let each user have their own unique todolist. I don't want users to see each other's todolists. I am new to python and django and I am not sure how to go about doing this. I have created a test user using the admin site and when I log in using this test user, I am taken to the page with the same (and only) todo list. I have not yet implemented a registration page for new users and I want to be able to link users to their own todolists before I do that. This is what I have so far:
// models.py
class Todo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
text = models.TextField()
created_at = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.title
#receiver(post_save, sender=User)
def create_user_todos(sender, instance, created, **kwargs):
if created:
Todo.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_todos(sender, instance, **kwargs):
instance.todo.save()
// views.py
def index(request, user_id):
user = User.objects.get(pk=user_id)
todos = user.todo.objects.all()[:10]
context = {
'todos': todos # to pass them to index.html
}
return render(request, 'index.html', context)
def details(request, id, user_id):
user = User.objects.get(pk=user_id)
todo = user.todo.objects.get(id=id)
context = {
'todo': todo
}
return render(request, 'details.html', context)
def add(request, user_id):
user = User.objects.get(pk=user_id)
if(request.method == 'POST'):
title = request.POST['title']
text = request.POST['text']
todo = user.todo(title=title, text=text)
todo.save()
return redirect('/todos')
else:
return render(request, 'add.html')
def edit(request, id, user_id):
user = User.objects.get(pk=user_id)
todo = user.todo.objects.get(id=id)
context = {
'todo': todo
}
if(request.method == 'POST'):
title = request.POST['title']
text = request.POST['text']
if len(str(title)) != 0:
todo.title = title
if len(str(text)) != 0:
todo.text = text
todo.save()
return redirect('/todos')
else:
return render(request, 'edit.html', context)
def delete(request, id, user_id):
user = User.objects.get(pk=user_id)
todo = user.todo.objects.get(id=id)
context = {
'todo': todo
}
if(request.method == 'POST'):
Todo.delete(todo)
return redirect('/todos')
else:
return render(request, 'delete.html', context)
I was following a tutorial beforehand where I added the extra parameter user_id to all my functions in views.py. It is giving me an error now but if I don't include the paramter and change my implementation of the functions to be
todo = Todo.objects.get(id=id)
then everything works.
I realize my question is vague and I have tried searching for solutions but everything I have read assumes I have some previous in depth knowledge of django (which I don't). Please lead me in the right direction :)
Related
I'm learning Django, so I'm trying to create a mini-app for practice.
I've 2 tables on DB:
By one side webapp_domicilio, where is a PK for the ID and some varchars for address info.
By the other side, webapp_persona, where is a PK for the ID, some varchars for person info and a FK referencing the address ID (webapp_domicilio.id)
I created the webpage for adding new item to webapp_persona, inheriting ModelForm and now I want to add the option to create, edit and delete an address without exiting this page, just like the Django Admin page.
Searching on stackoverflow I found this article: Creating front-end forms like in Django Admin but the accepted answer get me to a 404 page on Django documentation.
Then I tried what's in https://django-addanother.readthedocs.io/en/latest/index.html and I managed to insert the 'add' button but it doesn't open in a popup using CreatePopupMixin from django_addanother.views. There is a point where they explain how to make the view popup compatible but I don't really understand it. I also tried to insert the 'edit' button but it doesn't work neither.
I let my code here:
views.py:
def nueva_persona(request):
if request.method == 'POST':
formulario_persona = PersonaForm(request.POST)
if formulario_persona.is_valid():
formulario_persona.save()
return redirect('personas')
else:
formulario_persona = PersonaForm()
return render(request, 'nueva_persona.html', {'formulario_persona': formulario_persona})
def editar_domicilio(request, id):
domicilio = get_object_or_404(Domicilio, pk=id)
if request.method == 'POST':
formulario_domicilio = DomicilioForm(request.POST, instance=domicilio)
if formulario_domicilio.is_valid():
formulario_domicilio.save()
return redirect('domicilios')
else:
formulario_domicilio = DomicilioForm(instance=domicilio)
return render(request, 'editar_domicilio.html', {'formulario_domicilio': formulario_domicilio})
def nuevo_domicilio(request):
if request.method == 'POST':
formulario_domicilio = DomicilioForm(request.POST)
if formulario_domicilio.is_valid():
formulario_domicilio.save()
return redirect('domicilios')
else:
formulario_domicilio = DomicilioForm()
return render(request, 'nuevo_domicilio.html', {'formulario_domicilio': formulario_domicilio})
def eliminar_domicilio(request, id):
domicilio = get_object_or_404(Domicilio, pk=id)
if domicilio:
domicilio.delete()
return redirect('domicilios')
models.py:
class Domicilio(Model):
calle = CharField(max_length=255)
nro = IntegerField()
localidad = CharField(max_length=255)
def __str__(self):
return f'{self.calle} {self.nro} {self.localidad}'
class Persona(Model):
nombre = CharField(max_length=255)
apellido = CharField(max_length=255)
email = CharField(max_length=255)
domicilio = ForeignKey(Domicilio, on_delete=RESTRICT)
def __str__(self):
return f'{self.id} {self.nombre} {self.apellido} {self.email}'
forms.py:
class PersonaForm(ModelForm):
class Meta():
model = Persona
fields = '__all__'
widgets = {
'email': EmailInput(attrs={'type':'email'}),
'domicilio': AddAnotherWidgetWrapper(
Select,
reverse_lazy('nuevo_domicilio'),
),
# 'domicilio': EditSelectedWidgetWrapper(
# Select,
# reverse_lazy('editar_domicilio', args=['__fk__']),
# ),
}
class DomicilioForm(ModelForm):
class Meta():
model = Domicilio
fields = '__all__'
widgets = {
'nro': NumberInput(attrs={'type':'number'}),
}
What I'd like to do is create a form similar to this:
When user try to create a new person, it should open a form like this:
And, if user click on '+' button, it should open a popup like this:
Once user saves the new address, it should close the popup and show it in drop down list of the 'domicilio' field
Thaks and regards
I have a model Client which uses a #receiver signal to update its fields whenever a User is created, so it creates a Client profile.
class Client(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
address = models.CharField(max_length=200, verbose_name="Morada")
city = models.CharField(max_length=200, verbose_name="Cidade")
postal = models.CharField(max_length=8, validators=[RegexValidator(r'^\d{4}(-\d{3})?$')], verbose_name="Código Postal")
nif = models.CharField(max_length=9, verbose_name="NIF", validators=[RegexValidator(r'^\d{1,10}$')], unique=True, null=True)
mobile = models.CharField(max_length=9, verbose_name="Telemóvel", validators=[RegexValidator(r'^\d{1,10}$')])
def __str__(self):
return "%s %s" % (self.user.first_name, self.user.last_name)
class Meta:
verbose_name_plural = "Clientes"
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Clients.objects.create(user=instance)
instance.clients.save()
Is there a way to only run this if the user created belongs to the Clients group? Because if a user is created in the Employees group, I don't want to create a profile.
This is the view that creates the Client in the Clients group:
#login_required(login_url='./accounts/login/')
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save() # this creates the user with first_name, email and last_name as well!
group = Group.objects.get(name='Clients')
user.groups.add(group)
user.refresh_from_db() # load the profile instance created by the signal
user.clients.address = form.cleaned_data.get('address')
user.clients.city = form.cleaned_data.get('city')
user.clients.postal = form.cleaned_data.get('postal')
user.clients.nif = form.cleaned_data.get('nif')
user.clients.mobile = form.cleaned_data.get('mobile')
return redirect('clients')
else:
form = SignUpForm()
return render(request, 'backend/new_client.html', {'form': form})
Doing it in the view (without signal):
#login_required(login_url='./accounts/login/')
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
group = Group.objects.get(name='Clients')
user.groups.add(group)
client = Client.objects.create(
user=user,
address=form.cleaned_data.get('address')
city=form.cleaned_data.get('city')
postal=form.cleaned_data.get('postal')
nif=form.cleaned_data.get('nif')
mobile=form.cleaned_data.get('mobile')
)
return redirect('clients')
else:
form = SignUpForm()
return render(request, 'backend/new_client.html', {'form': form})
Then you can choose to move all the code under user = form.save() in the form's itself (I assume it's a custom ModelForm):
# forms.py
class SignUpForm(models.Form):
# your existing code here
def save(self):
# NB if you're still using py2 you'll need
# `user = super(SignUpForm, self).save()` instead
user = super().save()
group = Group.objects.get(name='Clients')
user.groups.add(group)
cleaned_data = self.cleaned_data
client = Client.objects.create(
user=user,
address=cleaned_data.get('address')
city=cleaned_data.get('city')
postal=cleaned_data.get('postal')
nif=cleaned_data.get('nif')
mobile=cleaned_data.get('mobile')
)
return user
And your view becomes:
#login_required(login_url='./accounts/login/')
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
form.save()
return redirect('clients')
else:
form = SignUpForm()
return render(request, 'backend/new_client.html', {'form': form})
Both options are correct and functionnaly equivalent, but the second one is IMHO more maintainable - first because a form is easier to test than a view (you don't need to create a request object), and also because it encapsulate the whole domain logic in the same place (the form) instead of scattering it between the form and the view. The only downside is that you loose the ability to pass the commit=False arg to form.save(), but since this form has obviously no other purpose you wouldn't use this feature anyway.
I am trying to create an endpoint to edit both the user model and custom profile model below.
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500)
location = models.CharField(max_length=50)
image = models.ImageField(default='default.jpg', upload_to='profile')
In the regular django I would do:
views.py
def edit_profile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
extended_profile_form = ProfileForm(request.POST,
request.FILES,
instance=request.user.profile)
if form.is_valid() and extended_profile_form.is_valid():
form.save()
extended_profile_form.save()
return redirect('accounts:profile')
else:
form = EditProfileForm(instance=request.user)
extended_profile_form = ProfileForm(instance=request.user.profile)
context = {
'form':form,
'extended_profile_form':extended_profile_form
}
return render(request, 'accounts/edit-profile.html', context)
what is the equivalent for django rest framework?
I have tried:
views.py (Django Rest Framework)
#api_view(['GET','PUT'])
def profile(request):
if request.method == 'GET':
user = User.objects.filter(username=request.user)
profile_user = Profile.objects.filter(user=request.user)
serializer_user = UserSerializer(user, many=True)
serializer_profile_user = ProfileSerializer(profile_user, many=True)
result = {'serializer_user': serializer_user.data, 'serializer_profile_user': serializer_profile_user.data}
return Response(result)
elif request.method == 'PUT':
user = User.objects.filter(username=request.user)
profile_user = Profile.objects.filter(user=request.user)
serializer_user = UserSerializer(user, data=request.data)
serializer_profile_user = ProfileSerializer(profile_user, data=request.data)
if serializer_user.is_valid() and serializer_profile_user.is_valid():
serializer_user.save()
serializer_profile_user.save()
result = {'serializer_user': serializer_user.data, 'serializer_profile_user': serializer_profile_user.data}
return Response(result)
result = {'serializer_user': serializer_user.data, 'serializer_profile_user': serializer_profile_user.data}
return Response(result.errors, status=status.HTTP_400_BAD_REQUEST)
When I am browsing the endpoint, it does display serializer_user and serializer_profile_user data but I am unable to edit any of those data using the DRF browsable API.
Am I right thinking the codes above is the equivalent of the codes from the codes from the normal django to edit the profile of the user?
It looks fine to me, but you need to replace this:
if request.method == 'GET':
user = User.objects.filter(username=request.user)
with this:
if request.method == 'GET':
try:
user = User.objects.get(id=request.user.id)
except User.DoesNotExist:
return Response(data='no such user!', status=status.HTTP_400_BAD_REQUEST)
# you need to use objects.get because objects.filter returns a queryset not an abject
Because, request.user is an instance of User model, you cannot compare it to an attribute of user (in your case username)
PS: same goes with your PUT method as well.
Hope this helps!
Look. You can make it easier. Let's take Post model (for example):
class Post(models.Model):
author = models.ForeignKey(base.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
text = models.TextField()
likes = models.ManyToManyField(base.AUTH_USER_MODEL, blank=True, related_name='post_likes')
created_date = models.DateTimeField(default=timezone.now)
And that You should describe it in your serializer (serializer is something similar to DTO. It converts data into a service-friendly JSON view):
class PostCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'text']
And the last part - Endpoint:
class PostUpdateView(UpdateAPIView):
serializer_class = PostCreateUpdateSerializer
def get_queryset(self):
return Post.objects.filter(author=self.request.user)
It will be more comfortable to use CBV for Django and DRF
And One more thing. You shouldn't create one more table for your user model. This is due to the extension of the BaseUser model. Link for help
hi i a working on a project for that i have made login and registration of a user. now i want to show full profile of user.since get_profile is not working anymore so how can i get full profile of a user
my models.py
class Consultants(models.Model):
consul_id=models.IntegerField(default=0,primary_key=True)
first_name=models.CharField(max_length=255,blank=True,null=True)
last_name=models.CharField(max_length=255,blank=True,null=True)
email=models.EmailField(max_length=255,blank=True,null=True)
username=models.CharField(max_length=255,blank=True,null=True)
password=models.CharField(max_length=50,blank=True,null=True)
last_login=models.DateTimeField(default=datetime.now,blank=True,null=True)
is_active=models.BooleanField(default=False)
def __str__(self):
return self.first_name
views.py for login and registration
def register(request):
context = RequestContext(request)
registered = False
if request.method == 'POST':
# user_form = UserForm(data=request.POST)
consultant_form = ConsultantsForm(data=request.POST)
if consultant_form.is_valid():
consultant = consultant_form.save(commit=False)
consultant.save()
registered = True
else:
print consultant_form.errors
else:
consultant_form = ConsultantsForm()
return render_to_response(
'register.html',
{'consultant_form': consultant_form, 'registered': registered},
context_instance=RequestContext(request))
def login_user(request):
context = RequestContext(request)
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
print type(username)
print "username",username
try:
user = Consultants.objects.get(Q(username= username) & Q(password= password))
print 'chala'
if user.is_active:
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return HttpResponse("welcome......you are succesfuly log in")
else:
return HttpResponse("Your UkKonnect account is disabled.")
except ObjectDoesNotExist:
return HttpResponse("INvalid User")
else:
return render_to_response('login.html', {}, context)
i want to make def full_profile and def edit_profile.
How can i get logged in user consul_id??
please help me
Not sure that I understand you problem well.. Take a look at recommended way of extending User model:
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
Then you can do:
u = User.objects.get(username='fsmith')
freds_department = u.employee.department
In your case it would be:
class Consultant(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
*your fields here*
Now you can use standard authentication forms and methods
You always can obtain consultant data as:
request.user.consultant
i have a problem when i try update the data for model usuario
this is my model
class Usuario(models.Model):
user = models.ForeignKey(User)
nombres = models.CharField(max_length = 50)
correo = models.EmailField()
class Meta:
db_table = u'utp_users'
def __str__(self):
return str(self.nombres.encode('utf-8') )
this is mi form. py
class UsuarioForm(forms.ModelForm):
class Meta:
model = Usuario
fields = ['nombres','correo']
my url
url(r'^menu/update/(?P<usuario_id>\d+)$', 'utpapp.views.update'),
this my view update
#login_required(login_url='/')
def update(request,usuario_id):
form = UsuarioForm(request.POST)
if form.is_valid():
user = Usuario.objects.get(pk=usuario_id)
form = UsuarioForm(request.POST, instance = user)
form.save()
return redirect('/menu/')
else:
user = Usuario.objects.get(pk = usuario_id)
form = UsuarioForm(instance=user)
return render_to_response('form.html',{ 'form':form }, context_instance=RequestContext(request))
and the template i am do this
<a title="Editar informacion" href="/menu/update/{{usuario.id}}"><img src="/media/imagenes/usuario.png" class=" col-xs-3 col-md-7 quitar-float" alt="Editar informacion" ></a>
the problem is when i select the option update i get the this msj "Page not found (404)"
but i change in template href {{usuario.id}} for {{user.id}} is work but with user different what is the problem ??
Try this, you need to pass usuario as context to the template, and you can much simplify the code (especially the form for GET/POST, they are basically the same form, so don't repeat it) as like:
from django.shortcuts import get_object_or_404
#login_required(login_url='/')
def update(request,usuario_id):
user = get_object_or_404(Usuario, pk=usuario_id)
form = UsuarioForm(request.POST or None, instance=user)
if request.method == 'POST' and form.is_valid():
form.save()
return redirect('/menu/')
# you need to pass `usuario` as part of the context to template
# so you can access usuario.id in template
return render_to_response('form.html',{ 'form':form, 'usuario': user }, context_instance=RequestContext(request))
Also make sure you have a url route for /menu/, or else redirect will get you 404.
You forget to pass the usuario variable to the template.