草庐IT

篇(15)-Asp.Net Core入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

荣景智工 2023-03-28 原文

篇(15)-Asp.Net Core入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

在上个篇章中,讲了角色和菜单的关系(也就是给角色赋权),本章讲用户和给用户分派角色的功能。如果是小白,最好是仔细看我写的代码,因为关键代码处都有注解。建议将篇14和篇15阅读完毕再做演练,为防止单篇过长,我将其分成2篇来讲解。

用户与角色的处理逻辑是:(1).用户的增删改查;(2).给用户选一个所属角色。

1.用户管理功能

(1).用户表(Sql库)的创建

CREATE TABLE [dbo].[Manager](
[Id] [int] IDENTITY(1,1) NOT NULL,
[RoleId] [int] NOT NULL,
[UserName] [varchar](32) NOT NULL,
[Password] [varchar](128) NOT NULL,
[Avatar] [varchar](256) NULL,
[NickName] [varchar](32) NULL,
[Mobile] [varchar](16) NULL,
[Email] [varchar](128) NULL,
[LoginCount] [int] NULL,
[LoginLastIp] [varchar](64) NULL,
[LoginLastTime] [datetime] NULL,
[AddManagerId] [int] NOT NULL,
[AddTime] [datetime] NOT NULL,
[ModifyManagerId] [int] NULL,
[ModifyTime] [datetime] NULL,
[IsLock] [bit] NOT NULL,
[IsDelete] [bit] NOT NULL,
[Remark] [varchar](128) NULL,
CONSTRAINT [PK_MANAGER] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

 

(2).用户Model的编写,这个Model直接与Sql表的结构一致。

public class Manager
{
/// <summary>
/// 主键 MaxLength属性作用于字符串,不能用在int类型上
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// 角色ID
/// </summary>
public int RoleId { get; set; }
/// <summary>
/// 用户名
/// </summary>
[Required]
public String UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public String Password { get; set; }
/// <summary>
/// 头像
/// </summary>
public String Avatar { get; set; }
/// <summary>
/// 用户昵称
/// </summary>
public String NickName { get; set; }
/// <summary>
/// 手机号码
/// </summary>
public String Mobile { get; set; }
/// <summary>
/// 邮箱地址
/// </summary>
public String Email { get; set; }
/// <summary>
/// 登录次数
/// </summary>
public int? LoginCount { get; set; }
/// <summary>
/// 最后一次登录IP
/// </summary>
public String LoginLastIp { get; set; }
/// <summary>
/// 最后一次登录时间
/// </summary>
public DateTime? LoginLastTime { get; set; }
/// <summary>
/// 添加人
/// </summary>
[Required]
public int AddManagerId { get; set; }
/// <summary>
/// 添加时间
/// </summary>
[Required]
public DateTime AddTime { get; set; }
/// <summary>
/// 修改人
/// </summary>
public int? ModifyManagerId { get; set; }
/// <summary>
/// 修改时间
/// </summary>
[MaxLength(23)]
public DateTime? ModifyTime { get; set; }
/// <summary>
/// 是否锁定
/// </summary>
[Required]
public Boolean IsLock { get; set; }
/// <summary>
/// 是否删除
/// </summary>
[Required]
public Boolean IsDelete { get; set; }
/// <summary>
/// 备注
/// </summary>
public String Remark { get; set; }
}

 

(3).用户View部分的编写

(3.1)视图View部分包括用户的增、删、改、查功能,还有对应的修改用户角色,修改用户密码。

(3.2)Create视图代码如下

@{ ViewData["Title"] = "新建用户"; }
@model RegisterManagerView
@section Scripts{
<script type="text/javascript" src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script type="text/javascript" src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
<form action="/Manager/Create" method="post">
@Html.AntiForgeryToken()
<div>
<label asp-for="UserName">用户名</label>
<div>
<input type="text" asp-for="UserName" name="UserName" placeholder="用户名">
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="Password">密码</label>
<div>
<input type="password" asp-for="Password" name="Password" placeholder="密码" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="ConfirmPassword">确认密码</label>
<div>
<input type="password" asp-for="ConfirmPassword" name="ConfirmPassword" placeholder="确认密码" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="Mobile">手机号</label>
<div>
<input type="text" asp-for="Mobile" name="Mobile" placeholder="手机号">
<span asp-validation-for="Mobile" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="Email">Email</label>
<div>
<input type="text" asp-for="Email" name="Email" placeholder="邮箱">
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="Remark">介绍</label>
<div>
<textarea name="Remark" asp-for="Remark" placeholder="相关介绍"></textarea>
</div>
</div>
<div>
<div>
<button type="submit">确定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(3.3)Edit视图代码如下

@{ ViewData["Title"] = "编辑用户"; }
@model EditManagerView
@section Scripts{
<script type="text/javascript" src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script type="text/javascript" src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
<form action="/Manager/Edit" method="post">
@Html.AntiForgeryToken()
<div>
<label asp-for="UserName">用户名</label>
<div>
<input type="text" asp-for="UserName" name="UserName" placeholder="用户名">
<span asp-validation-for="UserName" class="text-danger"></span>
<input type="hidden" asp-for="Id" />
</div>
</div>
<div>
<label asp-for="Mobile">手机号</label>
<div>
<input type="text" asp-for="Mobile" name="Mobile" placeholder="手机号">
<span asp-validation-for="Mobile" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="Email">Email</label>
<div>
<input type="text" asp-for="Email" name="Email" placeholder="邮箱">
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="Remark">介绍</label>
<div>
<textarea name="Remark" asp-for="Remark" placeholder="相关介绍"></textarea>
</div>
</div>
<div>
<div>
<button type="submit">确定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(3.4)Index视图代码如下(列表页)

@using Humanizer;
@using RjWebCms.Db;
@model PaginatedList<PageManager>
@{
ViewData["Title"] = "用户列表";
}
@section Scripts{
<script src="~/js/jquery-3.6.1.min.js"></script>
<script type="text/javascript">
function DelAll() {
var ids = document.getElementsByName("#chk_ids");
var arrIds = "";
var n = 0;
for (var i = 0; i < ids.length; i++) {
if (ids[i].checked == true) {
arrIds += ids[i].value + ",";
n++;
}
}
if (n == 0) {
alert("请选择要删除的信息");
return;
}

arrIds = arrids.substr(0, arrIds.length - 1);
//
if (confirm("确定要全部删除选择用户吗")) {
$.ajax({
type: "post",
url: "/Manager/DeleteAll",
data: { ids: arrIds },
success: function (data, state) {
alert('删除成功!');
window.location.href = "";
},

error: function (data, state) {
alert('删除失败');
}
});
}
}
</script>
}
<div class="panel panel-default todo-panel">
@Html.AntiForgeryToken()
<form asp-action="Index" method="get">
<table>
<tr><td><a asp-controller="Manager" asp-action="Create">添加</a></td></tr>
<tr>
<td>查询关键词:<input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" /></td>
<td><input type="submit" value="查询" /></td>
<td><a asp-action="Index">Back</a></td>
<td><a id="DelAll" name="DelAll">批量删除</a></td>
</tr>
</table>
</form>
<table class="table table-hover">
<thead>
<tr>
<td>&#x2714;</td>
<td><a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">用户名</a></td>
<td>角色名</td>
<td>手机号</td>
<td><a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">时间</a></td>
<td>操作</td>
</tr>
@foreach (var item in Model)
{
<tr>
<td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td>
<td>@item.UserName</td>
<td>@item.RoleName</td>
<td>@item.Mobile</td>
<td>@item.AddTime</td>
<td>
<a asp-controller="Manager" asp-action="Details" asp-route-id="@item.Id">View</a>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a>
<a asp-action="ChangeRole" asp-route-id="@item.Id">ChangeRole</a>
<a asp-action="ChangePass" asp-route-id="@item.Id">ChangePass</a>
<a asp-controller="Manager" asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</thead>
</table>
@{
var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.HasNextPage ? "disabled" : ""; ;
}
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex - 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @prevDisabled">
上一页
</a>
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex + 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @nextDisabled">
下一页
</a>
<div class="panel-footer add-item-form">
<!-- TODO: Add item form -->
</div>
</div>

 

(3.5)ChangePass视图代码

@model ChangePassView
@section Scripts{
<script type="text/javascript" src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script type="text/javascript" src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
<form action="/Manager/ChangePass" method="post">
@Html.AntiForgeryToken()
<div>
<label asp-for="OldPass">旧密码</label>
<input type="hidden" asp-for="Id" />
<div>
<input type="password" asp-for="OldPass" name="OldPass" placeholder="密码" />
<span asp-validation-for="OldPass" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="NewPass">新密码</label>
<div>
<input type="password" asp-for="NewPass" name="NewPass" placeholder="密码" />
<span asp-validation-for="NewPass" class="text-danger"></span>
</div>
</div>
<div>
<label asp-for="ConfirmNewPass">确认新密码</label>
<div>
<input type="password" asp-for="ConfirmNewPass" name="ConfirmNewPass" placeholder="确认密码" />
<span asp-validation-for="ConfirmNewPass" class="text-danger"></span>
</div>
</div>
<div>
<div>
<button type="submit">确定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(3.6)ChangeRole视图代码

@using RjWebCms.Models;
@{ ViewData["Title"] = "修改对应角色"; }
@model ChangeUserRole
<form action="/Manager/ChangeRole" method="post">
@Html.AntiForgeryToken()
<div>
<label asp-for="Id">选择对应角色</label>
<input type="hidden" asp-for="Id" />
<div>
@Html.DropDownList("ddl_RoleList", ViewBag.database as IEnumerable<SelectListItem>)
</div>
</div>
<div>
<div>
<button type="submit">确定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

 

(4).用户Controller部分的实现

public class ManagerController : Controller{
  private readonly IManagerService _manager;
  private readonly IManagerRoleService _managerRoleService;
  private readonly AppDbContext _appDbContext;
  public ManagerController(IManagerService manager,IManagerRoleService managerRoleService, AppDbContext appDbContext){
    _manager = manager;
    _managerRoleService = managerRoleService;
    _appDbContext = appDbContext;
  }
  public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber){
    ViewData["CurrentSort"] = sortOrder;
    ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  if (searchString != null)
  {
    pageNumber = 1;
  }
  else
  {
    searchString = currentFilter;
  }

#region 分页操作数据
ViewData["CurrentFilter"] = searchString;
var managers = from s in _appDbContext.Manager
join t in _appDbContext.ManagerRole on s.RoleId equals t.Id
select new PageManager{
Id=s.Id,
RoleId =s.RoleId,
UserName = s.UserName,
Email = s.Email,
Mobile = s.Mobile,
AddTime = s.AddTime,
RoleName = t.RoleName};
if (!string.IsNullOrEmpty(searchString))
{
managers = managers.Where(s => s.UserName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
managers = managers.OrderByDescending(s => s.UserName);
break;
case "Date":
managers = managers.OrderBy(s => s.AddTime);
break;
case "date_desc":
managers = managers.OrderByDescending(s => s.AddTime);
break;
default:
managers = managers.OrderBy(s => s.UserName);
break;
}

#endregion
int pageSize = 4;
return View(await PaginatedList<PageManager>.CreateAsync(managers.AsNoTracking(), pageNumber ?? 1, pageSize));
}

[HttpGet]
public IActionResult Create()
{
return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(RegisterManagerView manager)
{
if (ModelState.IsValid)
{
Manager mUser = new Manager
{
UserName = manager.UserName,//但暂时用重写的方式来处理
Password = AESEncryptHelper.Encode(manager.Password.Trim(), RjWebKeys.AesEncryptKeys), //对密码加密;
Mobile = manager.Mobile,
Email = manager.Email,
Remark = manager.Remark
};
//此处可以用AutoMapper进行转换
//因为AddManagerAsync的参数是Manager对象,而非RegisterManagerView对象
//否则就需要重写一个AddManagerAsync(RegisterManagerView manager) 这样的方法
var successful = await _manager.AddManagerAsync(mUser);
if (successful)
return RedirectToAction("Index");
else
return BadRequest("失败");
}
return View(manager);
}

[HttpGet]
public async Task<IActionResult> Edit(int id)
{
if (string.IsNullOrEmpty(id.ToString()))
return NotFound();
var m = await _manager.FindManagerAsync(id);
if (m == null)
return NotFound();
EditManagerView mView = new EditManagerView() {
Id = id,
UserName = m.UserName,
//Password = AESEncryptHelper.Decode(m.Password,RjWebKeys.AesEncryptKeys), //解密密码
//ConfirmPassword = AESEncryptHelper.Decode(m.Password, RjWebKeys.AesEncryptKeys), //解密密码
Mobile = m.Mobile,
Email = m.Email,
Remark = m.Remark
};
return View(mView);
}

[HttpPost]
public async Task<IActionResult> Edit(int id, EditManagerView editManager)
{
if (string.IsNullOrEmpty(id.ToString()))
return NotFound();
if (ModelState.IsValid)
{
try
{
//做个对象转换
Manager mUser = new Manager {
UserName = editManager.UserName,
Mobile = editManager.Mobile,
Email = editManager.Email,
Remark = editManager.Remark
};
//因为UpdateManagerAysnc方法参数为完整Manger对象,所以要转换
var result = await _manager.UpdateManagerAysnc(id, mUser);
if(result)
return RedirectToAction("Index");
else
return BadRequest("编辑失败");
}

catch (Exception ex)
{
return BadRequest("编辑失败");
}
}

return View();
}

public async Task<IActionResult> Details(int id)
{
var item = await _manager.FindManagerAsync(id);
return View(item);
}

public async Task<IActionResult> Delete(int id)
{
var result = await _manager.DeleteManagerAsync(id);
if (result)
return RedirectToAction("Index");
else
return Ok("删除失败!");
}
[HttpGet]
public async Task<IActionResult> ChangePass(int id)
{
if (string.IsNullOrEmpty(id.ToString()))
return NotFound();
//密码框的初始化也可以省略
var m = await _manager.FindManagerAsync(id);
if (m == null)
return NotFound();
ChangePassView cpView = new ChangePassView {
Id=id,
OldPass = AESEncryptHelper.Decode(m.Password, RjWebKeys.AesEncryptKeys) //解密密码m.Password,
};
return View(cpView);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePass(int id,ChangePassView cheView)
{
if (string.IsNullOrEmpty(id.ToString()))
return NotFound();
if (ModelState.IsValid)
{
//ChangePass方法在ManagerService中,注意参数对象
var successful = await _manager.ChangePass(id,cheView);
if (successful)
return RedirectToAction("Index");
else
return BadRequest("失败");
}
return View();
}

[HttpGet]
public async Task<IActionResult> ChangeRole(int id)
{
//本Action调用后,要初始化下拉框的选择
var user = await _manager.FindManagerAsync(id);
if (user == null)
return NotFound();
#region 绑定类别下拉框
var rolelist = await _managerRoleService.GetManagerRoleAsync();
var roleItems = new List<SelectListItem>()
{
new SelectListItem(){ Value="0",Text="全部",Selected=true}
};

foreach (var role in rolelist)
{
SelectListItem item = new SelectListItem() { Value = role.Id.ToString(), Text = role.RoleName };
roleItems.Add(item);
}
//遍历并选中(实现选中下拉框功能)
foreach (SelectListItem item in roleItems)
{
if (item.Value == user.RoleId.ToString())
item.Selected = true;
}
ViewBag.database = roleItems;
#endregion
return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangeRole(int id ,ChangeUserRole user)
{
//修改用户所属角色
if (string.IsNullOrEmpty(id.ToString()))
return NotFound();
#region 取下拉菜单值(RoleId)
string strRoleId = Request.Form["ddl_RoleList"];
if (!string.IsNullOrEmpty(strRoleId))
user.RoleId = int.Parse(strRoleId);
else
user.RoleId = 0;
#endregion
if (ModelState.IsValid)
{
try
{
//ChangeRole方法在ManagerService中,注意其参数
var result = await _manager.ChangeRole(id, user);
if (result)
return RedirectToAction("Index");
else
return BadRequest("编辑失败");
}

catch (Exception ex)
{
return BadRequest("编辑失败");
}
}
return View();
}

#region 验证功能

[HttpGet]
public async Task<IActionResult> CheckUserName(string UserName)
{
//result=true,表示有这个用户名,说明验证失败,无法添加
//那么return json 需要返回一个false
bool result = await _manager.CheckUserName(UserName);
return Json(!result); //返回结果必须是Json格式
}

[HttpGet]
public async Task<IActionResult> CheckMobile(string Mobile)
{
//result=true,表示有这个手机号,说明验证失败,无法添加
//那么return json 需要返回一个false
bool result = await _manager.CheckMobile(Mobile);
return Json(!result); //返回结果必须是Json格式
}

[HttpGet]
public async Task<IActionResult> CheckEmail(string Email)
{
//result=true,表示有这个邮箱,说明验证失败,无法添加
//那么return json 需要返回一个false
bool result = await _manager.CheckEmail(Email);
return Json(!result); //返回结果必须是Json格式
}

[HttpGet]
public async Task<IActionResult> CheckOldPass(int id,string oldpass)
{
//result=true,表示有这个旧密码,说明验证失败,无法添加
//那么return json 需要返回一个false
string strPass = AESEncryptHelper.Encode(oldpass.Trim(), RjWebKeys.AesEncryptKeys); //对密码加密;
bool result = await _manager.CheckOldPass(id,strPass);
return Json(result); //返回结果必须是Json格式
}
#endregion
}

 

2.用户分配角色

分配角色在ChangeRole视图页面完成,注意阅读其对应代码;

有关篇(15)-Asp.Net Core入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  4. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  5. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  6. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  7. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  8. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  9. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  10. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

随机推荐