sqlalchemy连接查询出现问题-从子节点获取下一个日期

时间:2019-06-13 18:47:51

标签: sqlalchemy flask-sqlalchemy

使用下面的示例,我试图进行一个查询以获取我的办公室列表,并从子表中提取下一次访问。

class Office(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    office_name = db.Column(db.String(100))

    visits = db.relationship('Visit', backref='office', lazy='select', order_by='desc(Visit.visit_date)')

class Visit(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    visit_date = db.Column(db.Date)

    office_id = db.Column(db.Integer, db.ForeignKey('office.id'))

我已经能够在原始SQL中创建查询,该查询将返回我需要的内容:

SELECT * FROM office 
LEFT OUTER JOIN ( SELECT office_id, visit_date FROM visit WHERE visit_date >= date('now') 
GROUP BY office_id ) 
AS next_vis ON id = next_vis.office_id

但是我无法在SQLAlchemy中转换以上内容。

最接近的是这个

next_vis = db.session.query(Visit.office_id, Visit.visit_date).filter(
        Visit.visit_date >= datetime.utcnow().date()).order_by(
            Visit.visit_date.asc()).group_by(Visit.office_id).subquery()

offices = db.session.query(Office, next_vis.c.visit_date).outerjoin(
    next_vis, Office.id == next_vis.c.office_id).order_by(
        Office.office_name.asc())

但是唯一的问题是它返回(办公室,日期)元组,但理想情况下我希望它作为单个对象返回。那不可能吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

如果有人感兴趣,我会采用一种稍微不同的方式。

我从联接查询切换为在Office模型中添加列属性:

struct MY_TOKEN_PRIVILEGES {
  DWORD PrivilegeCount;
  LUID_AND_ATTRIBUTES Privileges[2];
};

int RunUnderWinLogon(LPCWSTR executableWithSendInput)
{
  DWORD physicalConsoleSessionId = WTSGetActiveConsoleSessionId();

  auto winlogonPid = GetWinLogonPid(); // external function

  if (!winlogonPid)
  {
    std::cout << "ERROR getting winlogon pid" << std::endl;
    return 0;
  }

  HANDLE hWinlogonToken, hProcess;
  hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);

  if (!::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
    | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID
    | TOKEN_READ | TOKEN_WRITE, &hWinlogonToken))
  {
    printf("Process token open Error: %u\n", GetLastError());
  }

  // Is security descriptor needed for SendInput?
  PSECURITY_DESCRIPTOR pSD = NULL;

  SECURITY_ATTRIBUTES saToken;
  ZeroMemory(&saToken, sizeof(SECURITY_ATTRIBUTES));

  saToken.nLength = sizeof (SECURITY_ATTRIBUTES);
  saToken.lpSecurityDescriptor = pSD;
  saToken.bInheritHandle = FALSE;

  HANDLE hWinlogonTokenDup;
  if (!DuplicateTokenEx(hWinlogonToken, TOKEN_ALL_ACCESS, &saToken, SecurityImpersonation, TokenPrimary, &hWinlogonTokenDup))
  {
    printf("DuplicateTokenEx Error: %u\n", GetLastError());
  }

  if (!SetTokenInformation(hWinlogonTokenDup, TokenSessionId, (void*)physicalConsoleSessionId, sizeof(DWORD)))
  {
    printf("SetTokenInformation Error: %u\n", GetLastError());
  }

  //Adjust Token privilege
  LUID luidSeDebugName;
  if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidSeDebugName))
  {
    printf("Lookup Privilege value Error: %u\n", GetLastError());
  }

  LUID luidSeTcbName;
  if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luidSeTcbName))
  {
    printf("Lookup Privilege value Error: %u\n", GetLastError());
  }

  MY_TOKEN_PRIVILEGES tp;
  tp.PrivilegeCount = 2;
  tp.Privileges[0].Luid = luidSeDebugName;
  tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

  tp.Privileges[1].Luid = luidSeTcbName;
  tp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;

  if (!AdjustTokenPrivileges(hWinlogonTokenDup, FALSE, (PTOKEN_PRIVILEGES)&tp, /*BufferLength*/0, /*PreviousState*/(PTOKEN_PRIVILEGES)NULL, NULL))
  {
    printf("Adjust Privilege value Error: %u\n", GetLastError());
  }

  if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
  {
    printf("Token does not have the privilege\n");
  }

  DWORD creationFlags;
  creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

  LPVOID pEnv = NULL;
  if (CreateEnvironmentBlock(&pEnv, hWinlogonTokenDup, TRUE))
  {
    std::cout << "CreateEnvironmentBlock() success" << std::endl;
    creationFlags |= CREATE_UNICODE_ENVIRONMENT;
  }

  SECURITY_ATTRIBUTES saProcess, saThread;
  ZeroMemory(&saProcess, sizeof(SECURITY_ATTRIBUTES));
  ZeroMemory(&saThread, sizeof(SECURITY_ATTRIBUTES));

  saProcess.nLength = sizeof (SECURITY_ATTRIBUTES);
  saProcess.lpSecurityDescriptor = pSD;
  saProcess.bInheritHandle = FALSE;

  saThread.nLength = sizeof (SECURITY_ATTRIBUTES);
  saThread.lpSecurityDescriptor = pSD;
  saThread.bInheritHandle = FALSE;

  STARTUPINFO si;
  ZeroMemory(&si, sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);
  si.lpDesktop = (LPWSTR)L"winsta0\\default";

  PROCESS_INFORMATION pi;
  ZeroMemory(&pi, sizeof(pi));

  BOOL bResult = CreateProcessAsUser(
    hWinlogonTokenDup,   // client's access token
    executableWithSendInput,    // file using SendInput
    NULL,                 // command line
    &saProcess,            // pointer to process SECURITY_ATTRIBUTES
    &saThread,               // pointer to thread SECURITY_ATTRIBUTES
    FALSE,              // handles are not inheritable
    creationFlags,     // creation flags
    pEnv,               // pointer to new environment block
    NULL,               // name of current directory
    &si,               // pointer to STARTUPINFO structure
    &pi                // receives information about new process
  );
}

现在,当我执行Office.query.all()时,我可以做到:

class Office(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    office_name = db.Column(db.String(100))

    visits = db.relationship('Visit', backref='office', lazy='select', 
                order_by='desc(Visit.visit_date)')

    next_vis = column_property(
        select([Visit.visit_date]).where(
            and_(Visit.office_id == id, Visit.visit_date >= db.func.current_date())).order_by(
                Visit.visit_date.asc()).correlate_except(Visit))

如果我忽略了任何内容,请告诉我!

谢谢