Django费用追踪应用求助:自动填充用户+页面创建对象失败
问题描述
我开发了一款用户创建账户后可录入费用进行追踪的应用,系统要求填写customer、category、price、month四个字段。我希望customer字段能自动填充登录用户的username,无需用户从所有客户中选择。
但目前遇到问题:用户无法在页面创建对象,仅能通过admin面板创建。在页面创建时会抛出错误:
RelatedObjectDoesNotExist at / User has no customer.
我怀疑该问题与用户是在admin页面“认证与授权”板块下的Users中添加,而非由应用在Customers下创建有关(对象存储在Finance板块中)。
核心需求
- 实现
customer字段自动填充登录用户的username; - 修复页面无法创建对象的问题,恢复用户从前可直接在页面创建对象的功能。
相关代码文件
models.py
class Customer(models.Model): user = models.OneToOneField(User, null=True, on_delete=models.CASCADE) name = models.CharField(max_length=200, null=True) email = models.CharField(max_length=200, null=True, blank=True) date_created = models.DateTimeField(auto_now_add=True, null=True) # def __str__(self): # return self.name #I don't now if it's correct class Finance(models.Model): expenses_category = [ ("Saving", "Saving"), ("Food", "Food"), ("Bills", "Bills"), ("Rent", "Rent"), ("Extra", "Extra"), ] expenses_month = [ ("January", "January"), ("February", "February"), ("March", "March"), ("April", "April"), ("May", "May"), ("June", "June"), ("July", "July"), ("August", "August"), ("September", "September"), ("October", "October"), ("November", "November"), ("December", "December"), ] customer = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) category = models.CharField(choices=expenses_category, max_length=200) price = models.IntegerField() month = models.CharField(choices=expenses_month, max_length=200)
views.py
@csrf_exempt def registerPage(request): if request.user.is_authenticated: return redirect('home') else: form = CreateUserForm() if request.method == 'POST': form = CreateUserForm(request.POST) if form.is_valid(): # form.instance.user = request.user user = form.save() username = form.cleaned_data.get('username') group = Group.objects.get(name='customer') user.groups.add(group) messages.success(request, 'Account was created for ' + username) return redirect('login') context = {'form': form} return render(request, 'app_finance/register.html', context) def loginPage(request): username = None if request.user.is_authenticated: username = request.user.customer return redirect('home') else: if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(request, username=username, password=password) if user is not None: login(request, user) return redirect('home') else: messages.info(request, 'Username or password incorrect.') context = {} return render(request, 'app_finance/login.html', context) def logoutUser(request): logout(request) return redirect('login') def userPage(request): return render(request, 'app_finance/user.html') @login_required(login_url='login') def homeView(request): # customer = Customer.objects.get(id=pk) (not sure#) username = None items = Finance.objects.filter(customer_id=request.user.id) form = FinanceForm(initial={'customer': User}) if request.method == 'POST': username = request.user.customer form = FinanceForm(request.POST) # initial={'customer': user} if form.is_valid(): form.save() return HttpResponseRedirect('/') else: form = FinanceForm() return render(request, 'app_finance/home.html', {'form': form, 'items': items})
forms.py
class CustomerForm(ModelForm): class Meta: model = Customer fields = '__all__' exclude = ['user'] class CreateUserForm(UserCreationForm): class Meta: model = User fields = ['username', 'email', 'password1', 'password2'] class FinanceForm(ModelForm): class Meta: model = Finance fields = '__all__'
templates/home.html
<div> <span>Hello, {{ request.user }}</span> <br> <span><a class="hello-msg" href="{% url 'logout' %}">Logout</a></span> </div> <form action="" method="post"> {% csrf_token %} {{ form }} <!-- {{ form }} --> <input type="submit" value="Submit"> </form> <br> <div class="row"> <div class="col-md"> <div class="card card-body"> <h1>Expenses</h1> </div> <div class="card card-body"> <table class="table"> <tr> <th>User</th> <th>Category</th> <th>Price</th> <th>Month</th> </tr> {% for i in items %} <tr> <td>{{ i.customer }}</td> <td>{{ i.category }}</td> <td>{{ i.price }}</td> <td>{{ i.month }}</td> <td> </td> <td><a class="btn btn-sm btn-info" href="">Update</a></td> <td><a class="btn btn-sm btn-danger" href="">Delete</a></td> </tr> {% endfor %} </table> </div> </div> </div>
修复方案
1. 解决User has no customer错误
错误根源是新注册的User没有关联的Customer对象(一对一关系未建立),需要在用户注册时自动创建Customer:
修改views.py中的registerPage函数:
@csrf_exempt def registerPage(request): if request.user.is_authenticated: return redirect('home') else: form = CreateUserForm() if request.method == 'POST': form = CreateUserForm(request.POST) if form.is_valid(): user = form.save() username = form.cleaned_data.get('username') # 自动创建关联的Customer对象 Customer.objects.create(user=user, name=username, email=form.cleaned_data.get('email')) group = Group.objects.get(name='customer') user.groups.add(group) messages.success(request, 'Account was created for ' + username) return redirect('login') context = {'form': form} return render(request, 'app_finance/register.html', context)
2. 实现customer字段自动填充并隐藏
首先修改forms.py的FinanceForm,移除customer字段,避免用户手动选择:
class FinanceForm(ModelForm): class Meta: model = Finance fields = ['category', 'price', 'month'] # 只保留需要用户输入的字段
然后修改views.py的homeView,在保存Finance对象时自动关联当前登录用户:
@login_required(login_url='login') def homeView(request): items = Finance.objects.filter(customer_id=request.user.id) if request.method == 'POST': form = FinanceForm(request.POST) if form.is_valid(): finance = form.save(commit=False) # 暂不保存到数据库 finance.customer = request.user # 关联当前登录用户 finance.save() return HttpResponseRedirect('/') else: form = FinanceForm() return render(request, 'app_finance/home.html', {'form': form, 'items': items})
3. 修复loginPage中的错误
loginPage中username = request.user.customer会触发关联不存在的错误,直接移除该行:
def loginPage(request): if request.user.is_authenticated: return redirect('home') else: if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(request, username=username, password=password) if user is not None: login(request, user) return redirect('home') else: messages.info(request, 'Username or password incorrect.') context = {} return render(request, 'app_finance/login.html', context)
4. 完善Customer模型的__str__方法
让Customer对象在admin和页面中显示更友好:
class Customer(models.Model): user = models.OneToOneField(User, null=True, on_delete=models.CASCADE) name = models.CharField(max_length=200, null=True) email = models.CharField(max_length=200, null=True, blank=True) date_created = models.DateTimeField(auto_now_add=True, null=True) def __str__(self): return self.name or self.user.username # 优先显示name,为空则显示用户名
内容的提问来源于stack exchange,提问作者Francisco
相关产品推荐
相关产品推荐

